traccar各设备通讯协议分析(坐标转换、逆地址解析)

发表回复

确认码
输入您在图片中看到的字符,不需要区分大小写。
表情
:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :arrow: :| :mrgreen: :geek: :ugeek:

BBCode 允许
[img] 允许
[url] 允许
表情 允许

主题浏览
   

展开视图 主题浏览: traccar各设备通讯协议分析(坐标转换、逆地址解析)

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周日 7月 14, 2024 1:25 pm

传统web管理界面修改
在6.2版中“tc_servers、tc_users”2表中删除了“twelvehourformat”字段,所以要修改2个文件。
1. traccar-web/legacy/web/app/model/User.js
  1. /*
  2.  * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
  3.  *
  4.  * This program is free software: you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation, either version 3 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16.  */
  17.  
  18. Ext.define('Traccar.model.User', {
  19.     extend: 'Ext.data.Model',
  20.     identifier: 'negative',
  21.  
  22.     fields: [{
  23.         name: 'id',
  24.         type: 'int'
  25.     }, {
  26.         name: 'name',
  27.         type: 'string'
  28.     }, {
  29.         name: 'login',
  30.         type: 'string'
  31.     }, {
  32.         name: 'email',
  33.         type: 'string'
  34.     }, {
  35.         name: 'password',
  36.         type: 'string'
  37.     }, {
  38.         name: 'phone',
  39.         type: 'string'
  40.     }, {
  41.         name: 'readonly',
  42.         type: 'boolean'
  43.     }, {
  44.         name: 'administrator',
  45.         type: 'boolean'
  46.     }, {
  47.         name: 'map',
  48.         type: 'string'
  49.     }, {
  50.         name: 'latitude',
  51.         type: 'float'
  52.     }, {
  53.         name: 'longitude',
  54.         type: 'float'
  55.     }, {
  56.         name: 'zoom',
  57.         type: 'int'
  58.     // }, {
  59.     //     name: 'twelveHourFormat',
  60.     //     type: 'boolean'
  61.     }, {
  62.         name: 'coordinateFormat',
  63.         type: 'string'
  64.     }, {
  65.         name: 'disabled',
  66.         type: 'boolean'
  67.     }, {
  68.         name: 'expirationTime',
  69.         type: 'date',
  70.         dateFormat: 'c'
  71.     }, {
  72.         name: 'deviceLimit',
  73.         type: 'int'
  74.     }, {
  75.         name: 'userLimit',
  76.         type: 'int'
  77.     }, {
  78.         name: 'deviceReadonly',
  79.         type: 'boolean'
  80.     }, {
  81.         name: 'limitCommands',
  82.         type: 'boolean'
  83.     }, {
  84.         name: 'disableReports',
  85.         type: 'boolean'
  86.     }, {
  87.         name: 'poiLayer',
  88.         type: 'string'
  89.     }, {
  90.         name: 'attributes'
  91.     }],
  92.  
  93.     proxy: {
  94.         type: 'rest',
  95.         url: 'api/users',
  96.         writer: {
  97.             type: 'json',
  98.             writeAllFields: true
  99.         }
  100.     }
  101. });
2. traccar-web/legacy/web/app/model/Server.js
  1. /*
  2.  * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
  3.  *
  4.  * This program is free software: you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation, either version 3 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16.  */
  17.  
  18. Ext.define('Traccar.model.Server', {
  19.     extend: 'Ext.data.Model',
  20.     identifier: 'negative',
  21.  
  22.     fields: [{
  23.         name: 'id',
  24.         type: 'int'
  25.     }, {
  26.         name: 'registration',
  27.         type: 'boolean'
  28.     }, {
  29.         name: 'readonly',
  30.         type: 'boolean'
  31.     }, {
  32.         name: 'deviceReadonly',
  33.         type: 'boolean'
  34.     }, {
  35.         name: 'limitCommands',
  36.         type: 'boolean'
  37.     }, {
  38.         name: 'disableReports',
  39.         type: 'boolean'
  40.     }, {
  41.         name: 'map',
  42.         type: 'string'
  43.     }, {
  44.         name: 'bingKey',
  45.         type: 'string'
  46.     }, {
  47.         name: 'mapUrl',
  48.         type: 'string'
  49.     }, {
  50.         name: 'latitude',
  51.         type: 'float'
  52.     }, {
  53.         name: 'longitude',
  54.         type: 'float'
  55.     }, {
  56.         name: 'zoom',
  57.         type: 'int'
  58.     // }, {
  59.     //     name: 'twelveHourFormat',
  60.     //     type: 'boolean'
  61.     }, {
  62.         name: 'forceSettings',
  63.         type: 'boolean'
  64.     }, {
  65.         name: 'coordinateFormat',
  66.         type: 'string'
  67.     }, {
  68.         name: 'poiLayer',
  69.         type: 'string'
  70.     }, {
  71.         name: 'announcement',
  72.         type: 'string'
  73.     }, {
  74.         name: 'attributes'
  75.     }],
  76.  
  77.     proxy: {
  78.         type: 'ajax',
  79.         url: 'api/server',
  80.         actionMethods: {
  81.             update: 'PUT'
  82.         },
  83.         writer: {
  84.             type: 'json',
  85.             writeAllFields: true
  86.         }
  87.     }
  88. });

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周五 7月 05, 2024 9:11 am

协议处理程序,Python代码,gcj02ProtocolDecoder.py。
  1. # -*- coding: utf-8 -*-
  2.  
  3. import os
  4. import re
  5. from fnmatch import fnmatch
  6. from tracemalloc import start
  7.  
  8. protocolDir = '../src/main/java/org/traccar/protocol/'
  9. protocolDirGcj02 = '../gcj02/'
  10. filename = '*ProtocolDecoder.java'
  11. pattern1 = r'\s*position.setLatitude('
  12. pattern2 = r'\s*position.setLongitude('
  13.  
  14.  
  15. def gcj02Decode(oldFile, filename):
  16.     original_file = False
  17.  
  18.     with open(oldFile, 'r', encoding="utf-8") as file:
  19.         content = file.read()
  20.         file.close()
  21.         if content.find('position.lat_tag = true;') == -1 and content.find('position.setLatitude') >= 0:
  22.             content = content.replace(
  23.                 'position.setLatitude(', 'position.lat_tag = true; position.setLatitude(')
  24.             original_file = True
  25.         if content.find('position.lon_tag = true;') == -1 and content.find('position.setLongitude') >= 0:
  26.             content = content.replace(
  27.                 'position.setLongitude(', 'position.lon_tag = true; position.setLongitude(')
  28.             original_file = True
  29.  
  30.         if original_file:
  31.             with open(protocolDirGcj02+filename, mode='w', encoding="utf-8") as outfile:
  32.                 outfile.write(content)
  33.                 outfile.close()
  34.                 print('转换后的新文件:{}'.format(protocolDirGcj02+filename))
  35.  
  36.     return original_file
  37.  
  38.  
  39. if __name__ == '__main__':
  40.     log = open("d:/protocol-log .txt", mode="w", encoding="utf-8")
  41.     if not os.path.exists(protocolDirGcj02):
  42.         os.mkdir(protocolDirGcj02)
  43.  
  44.     # 遍历目录, 返回的是一个三元组(root,dirs,files)。
  45.     file_count = 0
  46.     for root, dirs, files in os.walk(protocolDir):
  47.         for file in files:
  48.             filePath = os.path.join(root, file)
  49.             if fnmatch(filePath, filename):   # 查找与filename相匹配的文件
  50.                 '''
  51.                开头部分:\033[显示方式;前景色;背景色m + 结尾部分:\033[0m
  52.                数值表示的参数含义:
  53.                    显示方式: 0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、 5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
  54.                    前景色: 30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋 红)、36(青色)、37(白色)
  55.                    背景色: 40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋 红)、46(青色)、47(白色)
  56.                '''
  57.                 print('匹配文件:%s' % (filePath), file=log)
  58.                 print('\n\033[1;37;42m 匹配文件:%s \033[0m' % (filePath))
  59.                 if gcj02Decode(filePath, file):
  60.                     file_count += 1
  61.         break
  62.     log.close()
  63.     print('\n\033[1;37;41m 共转换文件{}个! \033[0m'.format(file_count))

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周四 7月 04, 2024 10:01 pm

QQ的逆地址解析。QqGeocoder.java
  1. package org.traccar.geocoder;
  2.  
  3. import jakarta.json.JsonObject;
  4. import jakarta.ws.rs.client.Client;
  5.  
  6. public class QqGeocoder extends JsonGeocoder {
  7.     private static String formatUrl(String url, String key) {
  8.         if (url == null) {
  9.             url = "https://apis.map.qq.com/ws/geocoder/v1";
  10.         }
  11.  
  12.         if (key != null) {
  13.             url += "?key=" + key + "&location=%f,%f";
  14.         }
  15.         return url;
  16.     }
  17.  
  18.     public QqGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
  19.         super(client, formatUrl(url, key), cacheSize, addressFormat);
  20.     }
  21.  
  22.     @Override
  23.     public Address parseAddress(JsonObject json) {
  24.         Address address = new Address();
  25.         try {
  26.             String value = json.getJsonObject("result").getJsonObject("formatted_addresses").getString("recommend");
  27.             address.setFormattedAddress(value);
  28.             value = json.getJsonObject("result").getString("address");
  29.             address.setStreet(value);
  30.         } catch (Exception e) {
  31.             return null;
  32.         }
  33.         return address;
  34.     }
  35. }

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周四 7月 04, 2024 11:26 am

逆地址转换配置文件修改。traccar.xml 或 debug.xml,高德、QQ二选一
  1.     <!--  amap逆地址解析  -->
  2.     <entry key='geocoder.type'>amap</entry>
  3.     <entry key='geocoder.url'>https://restapi.amap.com/v3/geocode/regeo</entry>
  4.     <entry key='geocoder.key'>de6cf58608a044b643da48377c6bdbfb</entry>
  5.     <entry key='geocoder.format'>%f %r</entry>
  6.  
  7.     <!--  qq逆地址解析   -->
  8.     <entry key='geocoder.type'>qq</entry>
  9.     <entry key='geocoder.url'>https://apis.map.qq.com/ws/geocoder/v1</entry>
  10.     <entry key='geocoder.key'>CGABZ-7KUL2-WDEUO-CO6FL-DXBCE-SIFW2</entry>
  11.     <entry key='geocoder.format'>%f,%r</entry>
返回地址内容主要有以下方面,不过在本项目中只提供“%f %r”:
“%p”,邮政编码;
“%c”,国家;
“%s”,省,州;
“%d”,行政区;
“%t”,村落;
“%u”,郊区;
“%r”,街道;
“%h”,房子;
“%f”,格式化地址;

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周四 7月 04, 2024 11:20 am

增加高德地图、腾讯地图逆地址转换调用代码。MainModule.java
  1. /*
  2.  * Copyright 2018 - 2023 Anton Tananaev (anton@traccar.org)
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.traccar;
  17.  
  18. import com.fasterxml.jackson.databind.ObjectMapper;
  19. import com.fasterxml.jackson.databind.SerializationFeature;
  20. import com.fasterxml.jackson.datatype.jsonp.JSONPModule;
  21. import com.google.inject.AbstractModule;
  22. import com.google.inject.Injector;
  23. import com.google.inject.Provides;
  24. import com.google.inject.Scopes;
  25. import com.google.inject.name.Names;
  26. import io.netty.util.HashedWheelTimer;
  27. import io.netty.util.Timer;
  28. import org.apache.velocity.app.VelocityEngine;
  29. import org.traccar.broadcast.BroadcastService;
  30. import org.traccar.broadcast.MulticastBroadcastService;
  31. import org.traccar.broadcast.RedisBroadcastService;
  32. import org.traccar.broadcast.NullBroadcastService;
  33. import org.traccar.config.Config;
  34. import org.traccar.config.Keys;
  35. import org.traccar.database.LdapProvider;
  36. import org.traccar.database.OpenIdProvider;
  37. import org.traccar.database.StatisticsManager;
  38. import org.traccar.forward.EventForwarder;
  39. import org.traccar.forward.EventForwarderJson;
  40. import org.traccar.forward.EventForwarderAmqp;
  41. import org.traccar.forward.EventForwarderKafka;
  42. import org.traccar.forward.EventForwarderMqtt;
  43. import org.traccar.forward.PositionForwarder;
  44. import org.traccar.forward.PositionForwarderJson;
  45. import org.traccar.forward.PositionForwarderAmqp;
  46. import org.traccar.forward.PositionForwarderKafka;
  47. import org.traccar.forward.PositionForwarderRedis;
  48. import org.traccar.forward.PositionForwarderUrl;
  49. import org.traccar.forward.PositionForwarderMqtt;
  50. import org.traccar.geocoder.*;
  51. import org.traccar.geolocation.GeolocationProvider;
  52. import org.traccar.geolocation.GoogleGeolocationProvider;
  53. import org.traccar.geolocation.OpenCellIdGeolocationProvider;
  54. import org.traccar.geolocation.UnwiredGeolocationProvider;
  55. import org.traccar.handler.CopyAttributesHandler;
  56. import org.traccar.handler.FilterHandler;
  57. import org.traccar.handler.GeocoderHandler;
  58. import org.traccar.handler.GeolocationHandler;
  59. import org.traccar.handler.SpeedLimitHandler;
  60. import org.traccar.handler.TimeHandler;
  61. import org.traccar.helper.ObjectMapperContextResolver;
  62. import org.traccar.helper.WebHelper;
  63. import org.traccar.mail.LogMailManager;
  64. import org.traccar.mail.MailManager;
  65. import org.traccar.mail.SmtpMailManager;
  66. import org.traccar.session.cache.CacheManager;
  67. import org.traccar.sms.HttpSmsClient;
  68. import org.traccar.sms.SmsManager;
  69. import org.traccar.sms.SnsSmsClient;
  70. import org.traccar.speedlimit.OverpassSpeedLimitProvider;
  71. import org.traccar.speedlimit.SpeedLimitProvider;
  72. import org.traccar.storage.DatabaseStorage;
  73. import org.traccar.storage.MemoryStorage;
  74. import org.traccar.storage.Storage;
  75. import org.traccar.web.WebServer;
  76. import org.traccar.api.security.LoginService;
  77.  
  78. import jakarta.annotation.Nullable;
  79. import jakarta.inject.Singleton;
  80. import jakarta.ws.rs.client.Client;
  81. import jakarta.ws.rs.client.ClientBuilder;
  82. import java.io.IOException;
  83. import java.net.URISyntaxException;
  84. import java.net.http.HttpClient;
  85. import java.util.Properties;
  86.  
  87. public class MainModule extends AbstractModule {
  88.  
  89.     private final String configFile;
  90.  
  91.     public MainModule(String configFile) {
  92.         this.configFile = configFile;
  93.     }
  94.  
  95.     @Override
  96.     protected void configure() {
  97.         bindConstant().annotatedWith(Names.named("configFile")).to(configFile);
  98.         bind(Config.class).asEagerSingleton();
  99.         bind(Timer.class).to(HashedWheelTimer.class).in(Scopes.SINGLETON);
  100.     }
  101.  
  102.     @Singleton
  103.     @Provides
  104.     public static Storage provideStorage(Injector injector, Config config) {
  105.         if (config.getBoolean(Keys.DATABASE_MEMORY)) {
  106.             return injector.getInstance(MemoryStorage.class);
  107.         } else {
  108.             return injector.getInstance(DatabaseStorage.class);
  109.         }
  110.     }
  111.  
  112.     @Singleton
  113.     @Provides
  114.     public static ObjectMapper provideObjectMapper() {
  115.         ObjectMapper objectMapper = new ObjectMapper();
  116.         objectMapper.registerModule(new JSONPModule());
  117.         objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  118.         return objectMapper;
  119.     }
  120.  
  121.     @Singleton
  122.     @Provides
  123.     public static Client provideClient(ObjectMapperContextResolver objectMapperContextResolver) {
  124.         return ClientBuilder.newClient().register(objectMapperContextResolver);
  125.     }
  126.  
  127.     @Singleton
  128.     @Provides
  129.     public static SmsManager provideSmsManager(Config config, Client client) {
  130.         if (config.hasKey(Keys.SMS_HTTP_URL)) {
  131.             return new HttpSmsClient(config, client);
  132.         } else if (config.hasKey(Keys.SMS_AWS_REGION)) {
  133.             return new SnsSmsClient(config);
  134.         }
  135.         return null;
  136.     }
  137.  
  138.     @Singleton
  139.     @Provides
  140.     public static MailManager provideMailManager(Config config, StatisticsManager statisticsManager) {
  141.         if (config.getBoolean(Keys.MAIL_DEBUG)) {
  142.             return new LogMailManager();
  143.         } else {
  144.             return new SmtpMailManager(config, statisticsManager);
  145.         }
  146.     }
  147.  
  148.     @Singleton
  149.     @Provides
  150.     public static LdapProvider provideLdapProvider(Config config) {
  151.         if (config.hasKey(Keys.LDAP_URL)) {
  152.             return new LdapProvider(config);
  153.         }
  154.         return null;
  155.     }
  156.  
  157.     @Singleton
  158.     @Provides
  159.     public static OpenIdProvider provideOpenIDProvider(
  160.         Config config, LoginService loginService, ObjectMapper objectMapper
  161.         ) throws InterruptedException, IOException, URISyntaxException {
  162.         if (config.hasKey(Keys.OPENID_CLIENT_ID)) {
  163.             return new OpenIdProvider(config, loginService, HttpClient.newHttpClient(), objectMapper);
  164.         }
  165.         return null;
  166.     }
  167.  
  168.     @Provides
  169.     public static WebServer provideWebServer(Injector injector, Config config) {
  170.         if (config.getInteger(Keys.WEB_PORT) > 0) {
  171.             return new WebServer(injector, config);
  172.         }
  173.         return null;
  174.     }
  175.  
  176.     @Singleton
  177.     @Provides
  178.     public static Geocoder provideGeocoder(Config config, Client client, StatisticsManager statisticsManager) {
  179.         if (config.getBoolean(Keys.GEOCODER_ENABLE)) {
  180.             String type = config.getString(Keys.GEOCODER_TYPE);
  181.             String url = config.getString(Keys.GEOCODER_URL);
  182.             String key = config.getString(Keys.GEOCODER_KEY);
  183.             String language = config.getString(Keys.GEOCODER_LANGUAGE);
  184.             String formatString = config.getString(Keys.GEOCODER_FORMAT);
  185.             AddressFormat addressFormat = formatString != null ? new AddressFormat(formatString) : new AddressFormat();
  186.  
  187.             int cacheSize = config.getInteger(Keys.GEOCODER_CACHE_SIZE);
  188.             Geocoder geocoder;
  189.             switch (type) {
  190.                 case "amap":    //  高德逆地址转换调用
  191.                     geocoder = new AmapGeocoder(client, url, key, cacheSize, addressFormat);
  192.                     break;
  193.                 case "qq":      // 腾讯逆地址解码
  194.                     geocoder = new QqGeocoder(client, url, key, cacheSize, addressFormat);
  195.                     break;
  196.                 case "pluscodes":
  197.                     geocoder = new PlusCodesGeocoder();
  198.                     break;
  199.                 case "nominatim":
  200.                     geocoder = new NominatimGeocoder(client, url, key, language, cacheSize, addressFormat);
  201.                     break;
  202.                 case "locationiq":
  203.                     geocoder = new LocationIqGeocoder(client, url, key, language, cacheSize, addressFormat);
  204.                     break;
  205.                 case "gisgraphy":
  206.                     geocoder = new GisgraphyGeocoder(client, url, cacheSize, addressFormat);
  207.                     break;
  208.                 case "mapquest":
  209.                     geocoder = new MapQuestGeocoder(client, url, key, cacheSize, addressFormat);
  210.                     break;
  211.                 case "opencage":
  212.                     geocoder = new OpenCageGeocoder(client, url, key, language, cacheSize, addressFormat);
  213.                     break;
  214.                 case "bingmaps":
  215.                     geocoder = new BingMapsGeocoder(client, url, key, cacheSize, addressFormat);
  216.                     break;
  217.                 case "factual":
  218.                     geocoder = new FactualGeocoder(client, url, key, cacheSize, addressFormat);
  219.                     break;
  220.                 case "geocodefarm":
  221.                     geocoder = new GeocodeFarmGeocoder(client, key, language, cacheSize, addressFormat);
  222.                     break;
  223.                 case "geocodexyz":
  224.                     geocoder = new GeocodeXyzGeocoder(client, key, cacheSize, addressFormat);
  225.                     break;
  226.                 case "ban":
  227.                     geocoder = new BanGeocoder(client, cacheSize, addressFormat);
  228.                     break;
  229.                 case "here":
  230.                     geocoder = new HereGeocoder(client, url, key, language, cacheSize, addressFormat);
  231.                     break;
  232.                 case "mapmyindia":
  233.                     geocoder = new MapmyIndiaGeocoder(client, url, key, cacheSize, addressFormat);
  234.                     break;
  235.                 case "tomtom":
  236.                     geocoder = new TomTomGeocoder(client, url, key, cacheSize, addressFormat);
  237.                     break;
  238.                 case "positionstack":
  239.                     geocoder = new PositionStackGeocoder(client, key, cacheSize, addressFormat);
  240.                     break;
  241.                 case "mapbox":
  242.                     geocoder = new MapboxGeocoder(client, key, cacheSize, addressFormat);
  243.                     break;
  244.                 case "maptiler":
  245.                     geocoder = new MapTilerGeocoder(client, key, cacheSize, addressFormat);
  246.                     break;
  247.                 case "geoapify":
  248.                     geocoder = new GeoapifyGeocoder(client, key, language, cacheSize, addressFormat);
  249.                     break;
  250.                 case "geocodejson":
  251.                     geocoder = new GeocodeJsonGeocoder(client, url, key, language, cacheSize, addressFormat);
  252.                     break;
  253.                 default:
  254.                     geocoder = new GoogleGeocoder(client, key, language, cacheSize, addressFormat);
  255.                     break;
  256.             }
  257.             geocoder.setStatisticsManager(statisticsManager);
  258.             return geocoder;
  259.         }
  260.         return null;
  261.     }
  262.  
  263.     @Singleton
  264.     @Provides
  265.     public static GeolocationProvider provideGeolocationProvider(Config config, Client client) {
  266.         if (config.getBoolean(Keys.GEOLOCATION_ENABLE)) {
  267.             String type = config.getString(Keys.GEOLOCATION_TYPE, "google");
  268.             String url = config.getString(Keys.GEOLOCATION_URL);
  269.             String key = config.getString(Keys.GEOLOCATION_KEY);
  270.             switch (type) {
  271.                 case "opencellid":
  272.                     return new OpenCellIdGeolocationProvider(client, url, key);
  273.                 case "unwired":
  274.                     return new UnwiredGeolocationProvider(client, url, key);
  275.                 default:
  276.                     return new GoogleGeolocationProvider(client, key);
  277.             }
  278.         }
  279.         return null;
  280.     }
  281.  
  282.     @Singleton
  283.     @Provides
  284.     public static SpeedLimitProvider provideSpeedLimitProvider(Config config, Client client) {
  285.         if (config.getBoolean(Keys.SPEED_LIMIT_ENABLE)) {
  286.             String type = config.getString(Keys.SPEED_LIMIT_TYPE, "overpass");
  287.             String url = config.getString(Keys.SPEED_LIMIT_URL);
  288.             switch (type) {
  289.                 case "overpass":
  290.                 default:
  291.                     return new OverpassSpeedLimitProvider(config, client, url);
  292.             }
  293.         }
  294.         return null;
  295.     }
  296.  
  297.     @Singleton
  298.     @Provides
  299.     public static GeolocationHandler provideGeolocationHandler(
  300.             Config config, @Nullable GeolocationProvider geolocationProvider, CacheManager cacheManager,
  301.             StatisticsManager statisticsManager) {
  302.         if (geolocationProvider != null) {
  303.             return new GeolocationHandler(config, geolocationProvider, cacheManager, statisticsManager);
  304.         }
  305.         return null;
  306.     }
  307.  
  308.     @Singleton
  309.     @Provides
  310.     public static GeocoderHandler provideGeocoderHandler(
  311.             Config config, @Nullable Geocoder geocoder, CacheManager cacheManager) {
  312.         if (geocoder != null) {
  313.             return new GeocoderHandler(config, geocoder, cacheManager);
  314.         }
  315.         return null;
  316.     }
  317.  
  318.     @Singleton
  319.     @Provides
  320.     public static SpeedLimitHandler provideSpeedLimitHandler(@Nullable SpeedLimitProvider speedLimitProvider) {
  321.         if (speedLimitProvider != null) {
  322.             return new SpeedLimitHandler(speedLimitProvider);
  323.         }
  324.         return null;
  325.     }
  326.  
  327.     @Singleton
  328.     @Provides
  329.     public static CopyAttributesHandler provideCopyAttributesHandler(Config config, CacheManager cacheManager) {
  330.         if (config.getBoolean(Keys.PROCESSING_COPY_ATTRIBUTES_ENABLE)) {
  331.             return new CopyAttributesHandler(config, cacheManager);
  332.         }
  333.         return null;
  334.     }
  335.  
  336.     @Singleton
  337.     @Provides
  338.     public static FilterHandler provideFilterHandler(
  339.             Config config, CacheManager cacheManager, Storage storage, StatisticsManager statisticsManager) {
  340.         if (config.getBoolean(Keys.FILTER_ENABLE)) {
  341.             return new FilterHandler(config, cacheManager, storage, statisticsManager);
  342.         }
  343.         return null;
  344.     }
  345.  
  346.     @Singleton
  347.     @Provides
  348.     public static TimeHandler provideTimeHandler(Config config) {
  349.         if (config.hasKey(Keys.TIME_OVERRIDE)) {
  350.             return new TimeHandler(config);
  351.         }
  352.         return null;
  353.     }
  354.  
  355.     @Singleton
  356.     @Provides
  357.     public static BroadcastService provideBroadcastService(
  358.             Config config, ObjectMapper objectMapper) throws IOException {
  359.         if (config.hasKey(Keys.BROADCAST_TYPE)) {
  360.             switch (config.getString(Keys.BROADCAST_TYPE)) {
  361.                 case "multicast":
  362.                     return new MulticastBroadcastService(config, objectMapper);
  363.                 case "redis":
  364.                     return new RedisBroadcastService(config, objectMapper);
  365.                 default:
  366.                     break;
  367.             }
  368.         }
  369.         return new NullBroadcastService();
  370.     }
  371.  
  372.     @Singleton
  373.     @Provides
  374.     public static EventForwarder provideEventForwarder(Config config, Client client, ObjectMapper objectMapper) {
  375.         if (config.hasKey(Keys.EVENT_FORWARD_URL)) {
  376.             String forwardType = config.getString(Keys.EVENT_FORWARD_TYPE);
  377.             switch (forwardType) {
  378.                 case "amqp":
  379.                     return new EventForwarderAmqp(config, objectMapper);
  380.                 case "kafka":
  381.                     return new EventForwarderKafka(config, objectMapper);
  382.                 case "mqtt":
  383.                     return new EventForwarderMqtt(config, objectMapper);
  384.                 case "json":
  385.                 default:
  386.                     return new EventForwarderJson(config, client);
  387.             }
  388.         }
  389.         return null;
  390.     }
  391.  
  392.     @Singleton
  393.     @Provides
  394.     public static PositionForwarder providePositionForwarder(Config config, Client client, ObjectMapper objectMapper) {
  395.         if (config.hasKey(Keys.FORWARD_URL)) {
  396.             switch (config.getString(Keys.FORWARD_TYPE)) {
  397.                 case "json":
  398.                     return new PositionForwarderJson(config, client, objectMapper);
  399.                 case "amqp":
  400.                     return new PositionForwarderAmqp(config, objectMapper);
  401.                 case "kafka":
  402.                     return new PositionForwarderKafka(config, objectMapper);
  403.                 case "mqtt":
  404.                     return new PositionForwarderMqtt(config, objectMapper);
  405.                 case "redis":
  406.                     return new PositionForwarderRedis(config, objectMapper);
  407.                 case "url":
  408.                 default:
  409.                     return new PositionForwarderUrl(config, client, objectMapper);
  410.             }
  411.         }
  412.         return null;
  413.     }
  414.  
  415.     @Singleton
  416.     @Provides
  417.     public static VelocityEngine provideVelocityEngine(Config config) {
  418.         Properties properties = new Properties();
  419.         properties.setProperty("resource.loader.file.path", config.getString(Keys.TEMPLATES_ROOT) + "/");
  420.         properties.setProperty("web.url", WebHelper.retrieveWebUrl(config));
  421.  
  422.         VelocityEngine velocityEngine = new VelocityEngine();
  423.         velocityEngine.init(properties);
  424.         return velocityEngine;
  425.     }
  426.  
  427. }

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周四 7月 04, 2024 11:18 am

高德逆地址转换解析。AmapGeocoder.java
  1. package org.traccar.geocoder;
  2.  
  3. import jakarta.json.JsonObject;
  4. import jakarta.ws.rs.client.Client;
  5.  
  6. import static jakarta.json.JsonValue.ValueType.STRING;
  7.  
  8. public class AmapGeocoder extends JsonGeocoder {
  9.     private static String formatUrl(String url, String key) {
  10.         if (url == null) {
  11.             url = "https://restapi.amap.com/v3/geocode/regeo";
  12.         }
  13.         if (key != null) {
  14.             url += "?location=%f,%f&radius=1000&xtensions=base&output=json&key=" + key;
  15.         }
  16.         return url;
  17.     }
  18.  
  19.     public AmapGeocoder(Client client, String url, String key, int cacheSize, AddressFormat addressFormat) {
  20.         super(client, formatUrl(url, key), cacheSize, addressFormat);
  21.     }
  22.  
  23.     @Override
  24.     public Address parseAddress(JsonObject json) {
  25.         Address address = new Address();
  26.         try {
  27.             // 获取逆地理编码信息
  28.             JsonObject regeocode = json.getJsonObject("regeocode");
  29.             JsonObject addressComponent = regeocode.getJsonObject("addressComponent");
  30.  
  31.             // 获取格式化地址
  32.             String formattedAddress = regeocode.getString("formatted_address");
  33.             address.setFormattedAddress(formattedAddress);
  34.  
  35.             // 获取街道号码信息
  36.             String number = "", street = "";
  37.             JsonObject streetNumber = addressComponent.getJsonObject("streetNumber");
  38.             if (streetNumber.get("number").getValueType() == STRING)
  39.                 number = streetNumber.getString("number");
  40.             if (streetNumber.get("street").getValueType() == STRING)
  41.                 street = streetNumber.getString("street");
  42.             address.setStreet(street + " " + number);
  43.         } catch (Exception e) {
  44.             return null;
  45.         }
  46.         return address;
  47.     }
  48. }

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周四 7月 04, 2024 11:16 am

逆地址转换。JsonGeocoder.java
  1. /*
  2.  * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.traccar.geocoder;
  17.  
  18. import jakarta.ws.rs.client.Invocation;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;
  21. import org.traccar.database.StatisticsManager;
  22.  
  23. import jakarta.json.JsonObject;
  24. import jakarta.ws.rs.WebApplicationException;
  25. import jakarta.ws.rs.client.Client;
  26. import jakarta.ws.rs.client.InvocationCallback;
  27. import java.util.AbstractMap;
  28. import java.util.Collections;
  29. import java.util.LinkedHashMap;
  30. import java.util.Map;
  31.  
  32. public abstract class JsonGeocoder implements Geocoder {
  33.  
  34.     private static final Logger LOGGER = LoggerFactory.getLogger(JsonGeocoder.class);
  35.  
  36.     private final Client client;
  37.     private final String url;
  38.     private final AddressFormat addressFormat;
  39.     private StatisticsManager statisticsManager;
  40.  
  41.     private Map<Map.Entry<Double, Double>, String> cache;
  42.  
  43.     public JsonGeocoder(Client client, String url, final int cacheSize, AddressFormat addressFormat) {
  44.         this.client = client;
  45.         this.url = url;
  46.         this.addressFormat = addressFormat;
  47.         if (cacheSize > 0) {
  48.             this.cache = Collections.synchronizedMap(new LinkedHashMap<>() {
  49.                 @Override
  50.                 protected boolean removeEldestEntry(Map.Entry eldest) {
  51.                     return size() > cacheSize;
  52.                 }
  53.             });
  54.         }
  55.     }
  56.  
  57.     @Override
  58.     public void setStatisticsManager(StatisticsManager statisticsManager) {
  59.         this.statisticsManager = statisticsManager;
  60.     }
  61.  
  62.     protected String readValue(JsonObject object, String key) {
  63.         if (object.containsKey(key) && !object.isNull(key)) {
  64.             return object.getString(key);
  65.         }
  66.         return null;
  67.     }
  68.  
  69.     private String handleResponse(
  70.             double latitude, double longitude, JsonObject json, ReverseGeocoderCallback callback) {
  71.  
  72.         Address address = parseAddress(json);
  73.  
  74.         if (address != null) {
  75.             String formattedAddress = addressFormat.format(address);
  76.             if (cache != null) {
  77.                 cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress);
  78.             }
  79.             if (callback != null) {
  80.                 callback.onSuccess(formattedAddress);
  81.             }
  82.             return formattedAddress;
  83.         } else {
  84.             String msg = "Empty address. Error: " + parseError(json);
  85.             if (callback != null) {
  86.                 callback.onFailure(new GeocoderException(msg));
  87.             } else {
  88.                 LOGGER.warn(msg);
  89.             }
  90.         }
  91.         return null;
  92.     }
  93.  
  94.     @Override
  95.     public String getAddress(
  96.             final double latitude, final double longitude, final ReverseGeocoderCallback callback) {
  97.  
  98.         if (cache != null) {
  99.             String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude));
  100.             if (cachedAddress != null) {
  101.                 if (callback != null) {
  102.                     callback.onSuccess(cachedAddress);
  103.                 }
  104.                 return cachedAddress;
  105.             }
  106.         }
  107.  
  108.         if (statisticsManager != null) {
  109.             statisticsManager.registerGeocoderRequest();
  110.         }
  111.  
  112.         Invocation.Builder request;
  113.         if(url.contains("amap.com")) {
  114.             // 高德地图
  115.             request = client.target(String.format(url, longitude, latitude)).request();
  116.         }else {
  117.             // 其他地图
  118.             request = client.target(String.format(url, latitude, longitude)).request();
  119.         }
  120.  
  121.         if (callback != null) {
  122.             request.async().get(new InvocationCallback<JsonObject>() {
  123.                 @Override
  124.                 public void completed(JsonObject json) {
  125.                     handleResponse(latitude, longitude, json, callback);
  126.                 }
  127.  
  128.                 @Override
  129.                 public void failed(Throwable throwable) {
  130.                     callback.onFailure(throwable);
  131.                 }
  132.             });
  133.         } else {
  134.             try {
  135.                 return handleResponse(latitude, longitude, request.get(JsonObject.class), null);
  136.             } catch (WebApplicationException e) {
  137.                 LOGGER.warn("Geocoder network error", e);
  138.             }
  139.         }
  140.         return null;
  141.     }
  142.  
  143.     public abstract Address parseAddress(JsonObject json);
  144.  
  145.     protected String parseError(JsonObject json) {
  146.         return null;
  147.     }
  148.  
  149. }

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周三 7月 03, 2024 10:21 pm

GpsCoordinateUtils.java
  1. package org.traccar.handler;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.Arrays;
  6.  
  7. /**
  8.  * 地图坐标转换
  9.  * http://bbs.atoo.top:8081
  10.  */
  11. public class GpsCoordinateUtils {
  12.  
  13.     private static final double PI = 3.1415926535897932384626433832795;
  14.     //    private static final double PI = 3.14159265358979324;
  15.     private static final double A = 6378245.0;
  16.     private static final double EE = 0.00669342162296594323;
  17.  
  18.     /**
  19.      * 地球坐标系 WGS-84 to 火星坐标系 GCJ-02
  20.      *
  21.      * @param latitude  纬度
  22.      * @param longitude 经度
  23.      * @return [纬度, 经度]
  24.      */
  25.     public static double[] calWGS84toGCJ02(double latitude, double longitude) {
  26.         Point dev = calDev(latitude, longitude);
  27.         double retLat = latitude + dev.getLatitude();
  28.         double retLon = longitude + dev.getLongitude();
  29.         return new double[]{retLat, retLon};
  30.     }
  31.  
  32.     /**
  33.      * 地球坐标系 WGS-84 to 百度坐标系 BD-09
  34.      *
  35.      * @param latitude  纬度
  36.      * @param longitude 经度
  37.      * @return [纬度, 经度]
  38.      */
  39.     public static double[] calWGS84toBD09(double latitude, double longitude) {
  40.         Point dev = calDev(latitude, longitude);
  41.         double retLat = latitude + dev.getLatitude();
  42.         double retLon = longitude + dev.getLongitude();
  43.         return calGCJ02toBD09(retLat, retLon);
  44.     }
  45.  
  46.     /**
  47.      * 火星坐标系 GCJ-02 to 地球坐标系 WGS-84
  48.      *
  49.      * @param latitude  纬度
  50.      * @param longitude 经度
  51.      * @return [纬度, 经度]
  52.      */
  53.     public static double[] calGCJ02toWGS84(double latitude, double longitude) {
  54.         Point dev = calDev(latitude, longitude);
  55.         double retLat = latitude - dev.getLatitude();
  56.         double retLon = longitude - dev.getLongitude();
  57.         dev = calDev(retLat, retLon);
  58.         retLat = latitude - dev.getLatitude();
  59.         retLon = longitude - dev.getLongitude();
  60.         return new double[]{retLat, retLon};
  61.     }
  62.  
  63.     /**
  64.      * 百度坐标系 BD-09 to 地球坐标系 WGS-84
  65.      *
  66.      * @param latitude  纬度
  67.      * @param longitude 经度
  68.      * @return [纬度, 经度]
  69.      */
  70.     public static double[] calBD09toWGS84(double latitude, double longitude) {
  71.         double[] gcj = calBD09toGCJ02(latitude, longitude);
  72.         return calGCJ02toWGS84(gcj[0], gcj[1]);
  73.     }
  74.  
  75.     private static Point calDev(double latitude, double longitude) {
  76.         if (isOutOfChina(latitude, longitude, false)) {
  77.             return new Point(latitude, longitude);
  78.         }
  79.         double dLat = calLat(longitude - 105.0, latitude - 35.0);
  80.         double dLon = calLon(longitude - 105.0, latitude - 35.0);
  81.         double radLat = latitude / 180.0 * PI;
  82.         double magic = Math.sin(radLat);
  83.         magic = 1 - EE * magic * magic;
  84.         double sqrtMagic = Math.sqrt(magic);
  85.         dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
  86.         dLon = (dLon * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
  87.         return new Point(dLat, dLon);
  88.     }
  89.  
  90.     private static double calLat(double x, double y) {
  91.         double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
  92.         ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
  93.         ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
  94.         ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
  95.         return ret;
  96.     }
  97.  
  98.     private static double calLon(double x, double y) {
  99.         double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
  100.         ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
  101.         ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
  102.         ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
  103.         return ret;
  104.     }
  105.  
  106.     /**
  107.      * 火星坐标系 GCJ-02 to 百度坐标系 BD-09
  108.      *
  109.      * @param latitude  纬度
  110.      * @param longitude 经度
  111.      * @return [纬度, 经度]
  112.      */
  113.     public static double[] calGCJ02toBD09(double latitude, double longitude) {
  114.         double x = longitude, y = latitude;
  115.         double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * PI);
  116.         double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * PI);
  117.         double retLat = z * Math.sin(theta) + 0.006;
  118.         double retLon = z * Math.cos(theta) + 0.0065;
  119.         return new double[]{retLat, retLon};
  120.     }
  121.  
  122.     /**
  123.      * 百度坐标系 BD-09 to 火星坐标系 GCJ-02
  124.      *
  125.      * @param latitude  纬度
  126.      * @param longitude 经度
  127.      * @return [纬度, 经度]
  128.      */
  129.     public static double[] calBD09toGCJ02(double latitude, double longitude) {
  130.         double x = longitude - 0.0065, y = latitude - 0.006;
  131.         double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * PI);
  132.         double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * PI);
  133.         double retLat = z * Math.sin(theta);
  134.         double retLon = z * Math.cos(theta);
  135.         return new double[]{retLat, retLon};
  136.     }
  137.  
  138.     /**
  139.      * 判断坐标是否在国内
  140.      *
  141.      * @param latitude
  142.      * @param longitude
  143.      * @param precision 是否精确判断范围
  144.      * @return true 在国外,false 在国内
  145.      */
  146.     public static boolean isOutOfChina(double latitude, double longitude, boolean precision) {
  147.         if (precision) {
  148.             return CHINA_POLYGON.stream().noneMatch(point -> pointInPolygon(point, latitude, longitude));
  149.         } else {
  150.             if (longitude < 72.004 || longitude > 137.8347) {
  151.                 return true;
  152.             }
  153.             if (latitude < 0.8293 || latitude > 55.8271) {
  154.                 return true;
  155.             }
  156.             return false;
  157.         }
  158.     }
  159.  
  160.     /**
  161.      * 检查坐标点是否在多边形区域内
  162.      *
  163.      * @param polygon   多边形
  164.      * @param latitude  纬度
  165.      * @param longitude 经度
  166.      * @return true 在多边形区域内,false 在多边形区域外
  167.      */
  168.     private static boolean pointInPolygon(Point[] polygon, double latitude, double longitude) {
  169.         int i, j = polygon.length - 1;
  170.         boolean oddNodes = false;
  171.         for (i = 0; i < polygon.length; i++) {
  172.             if ((polygon[i].getLatitude() < latitude && polygon[j].getLatitude() >= latitude
  173.                     || polygon[j].getLatitude() < latitude && polygon[i].getLatitude() >= latitude)
  174.                     && (polygon[i].getLongitude() <= longitude || polygon[j].getLongitude() <= longitude)) {
  175.                 if (polygon[i].getLongitude()
  176.                         + (latitude - polygon[i].getLatitude()) / (polygon[j].getLatitude() - polygon[i].getLatitude())
  177.                         * (polygon[j].getLongitude() - polygon[i].getLongitude())
  178.                         < longitude) {
  179.                     oddNodes = !oddNodes;
  180.                 }
  181.             }
  182.             j = i;
  183.         }
  184.         return oddNodes;
  185.     }
  186.  
  187.     static class Point {
  188.         private double longitude;
  189.         private double latitude;
  190.  
  191.         Point(double latitude, double longitude) {
  192.             this.longitude = longitude;
  193.             this.latitude = latitude;
  194.         }
  195.  
  196.         public double getLongitude() {
  197.             return longitude;
  198.         }
  199.  
  200.         public void setLongitude(double longitude) {
  201.             this.longitude = longitude;
  202.         }
  203.  
  204.         public double getLatitude() {
  205.             return latitude;
  206.         }
  207.  
  208.         public void setLatitude(double latitude) {
  209.             this.latitude = latitude;
  210.         }
  211.  
  212.         @Override
  213.         public String toString() {
  214.             return longitude + "," + latitude;
  215.         }
  216.     }
  217.  
  218.     //region 中国行政边界的WGS84坐标数据
  219.     //Mainland
  220.     private static final Point[] MAINLAND = new Point[]{
  221.             new Point(27.32083, 88.91693),
  222.             new Point(27.54243, 88.76464),
  223.             new Point(28.00805, 88.83575),
  224.             new Point(28.1168, 88.62435),
  225.             new Point(27.86605, 88.14279),
  226.             new Point(27.82305, 87.19275),
  227.             new Point(28.11166, 86.69527),
  228.             new Point(27.90888, 86.45137),
  229.             new Point(28.15805, 86.19769),
  230.             new Point(27.88625, 86.0054),
  231.             new Point(28.27916, 85.72137),
  232.             new Point(28.30666, 85.11095),
  233.             new Point(28.59104, 85.19518),
  234.             new Point(28.54444, 84.84665),
  235.             new Point(28.73402, 84.48623),
  236.             new Point(29.26097, 84.11651),
  237.             new Point(29.18902, 83.5479),
  238.             new Point(29.63166, 83.19109),
  239.             new Point(30.06923, 82.17525),
  240.             new Point(30.33444, 82.11123),
  241.             new Point(30.385, 81.42623),
  242.             new Point(30.01194, 81.23221),
  243.             new Point(30.20435, 81.02536),
  244.             new Point(30.57552, 80.207),
  245.             new Point(30.73374, 80.25423),
  246.             new Point(30.96583, 79.86304),
  247.             new Point(30.95708, 79.55429),
  248.             new Point(31.43729, 79.08082),
  249.             new Point(31.30895, 78.76825),
  250.             new Point(31.96847, 78.77075),
  251.             new Point(32.24304, 78.47594),
  252.             new Point(32.5561, 78.40595),
  253.             new Point(32.63902, 78.74623),
  254.             new Point(32.35083, 78.9711),
  255.             new Point(32.75666, 79.52874),
  256.             new Point(33.09944, 79.37511),
  257.             new Point(33.42863, 78.93623),
  258.             new Point(33.52041, 78.81387),
  259.             new Point(34.06833, 78.73581),
  260.             new Point(34.35001, 78.98535),
  261.             new Point(34.6118, 78.33707),
  262.             new Point(35.28069, 78.02305),
  263.             new Point(35.49902, 78.0718),
  264.             new Point(35.50133, 77.82393),
  265.             new Point(35.6125, 76.89526),
  266.             new Point(35.90665, 76.55304),
  267.             new Point(35.81458, 76.18061),
  268.             new Point(36.07082, 75.92887),
  269.             new Point(36.23751, 76.04166),
  270.             new Point(36.66343, 75.85984),
  271.             new Point(36.73169, 75.45179),
  272.             new Point(36.91156, 75.39902),
  273.             new Point(36.99719, 75.14787),
  274.             new Point(37.02782, 74.56543),
  275.             new Point(37.17, 74.39089),
  276.             new Point(37.23733, 74.91574),
  277.             new Point(37.40659, 75.18748),
  278.             new Point(37.65243, 74.9036),
  279.             new Point(38.47256, 74.85442),
  280.             new Point(38.67438, 74.35471),
  281.             new Point(38.61271, 73.81401),
  282.             new Point(38.88653, 73.70818),
  283.             new Point(38.97256, 73.85235),
  284.             new Point(39.23569, 73.62005),
  285.             new Point(39.45483, 73.65569),
  286.             new Point(39.59965, 73.95471),
  287.             new Point(39.76896, 73.8429),
  288.             new Point(40.04202, 73.99096),
  289.             new Point(40.32792, 74.88089),
  290.             new Point(40.51723, 74.8588),
  291.             new Point(40.45042, 75.23394),
  292.             new Point(40.64452, 75.58284),
  293.             new Point(40.298, 75.70374),
  294.             new Point(40.35324, 76.3344),
  295.             new Point(41.01258, 76.87067),
  296.             new Point(41.04079, 78.08083),
  297.             new Point(41.39286, 78.39554),
  298.             new Point(42.03954, 80.24513),
  299.             new Point(42.19622, 80.23402),
  300.             new Point(42.63245, 80.15804),
  301.             new Point(42.81565, 80.25796),
  302.             new Point(42.88545, 80.57226),
  303.             new Point(43.02906, 80.38405),
  304.             new Point(43.1683, 80.81526),
  305.             new Point(44.11378, 80.36887),
  306.             new Point(44.6358, 80.38499),
  307.             new Point(44.73408, 80.51589),
  308.             new Point(44.90282, 79.87106),
  309.             new Point(45.3497, 81.67928),
  310.             new Point(45.15748, 81.94803),
  311.             new Point(45.13303, 82.56638),
  312.             new Point(45.43581, 82.64624),
  313.             new Point(45.5831, 82.32179),
  314.             new Point(47.20061, 83.03443),
  315.             new Point(46.97332, 83.93026),
  316.             new Point(46.99361, 84.67804),
  317.             new Point(46.8277, 84.80318),
  318.             new Point(47.0591, 85.52257),
  319.             new Point(47.26221, 85.70139),
  320.             new Point(47.93721, 85.53707),
  321.             new Point(48.39333, 85.76596),
  322.             new Point(48.54277, 86.59791),
  323.             new Point(49.1102, 86.87602),
  324.             new Point(49.09262, 87.34821),
  325.             new Point(49.17295, 87.8407),
  326.             new Point(48.98304, 87.89291),
  327.             new Point(48.88103, 87.7611),
  328.             new Point(48.73499, 88.05942),
  329.             new Point(48.56541, 87.99194),
  330.             new Point(48.40582, 88.51679),
  331.             new Point(48.21193, 88.61179),
  332.             new Point(47.99374, 89.08514),
  333.             new Point(47.88791, 90.07096),
  334.             new Point(46.95221, 90.9136),
  335.             new Point(46.57735, 91.07027),
  336.             new Point(46.29694, 90.92151),
  337.             new Point(46.01735, 91.02651),
  338.             new Point(45.57972, 90.68193),
  339.             new Point(45.25305, 90.89694),
  340.             new Point(45.07729, 91.56088),
  341.             new Point(44.95721, 93.5547),
  342.             new Point(44.35499, 94.71735),
  343.             new Point(44.29416, 95.41061),
  344.             new Point(44.01937, 95.34109),
  345.             new Point(43.99311, 95.53339),
  346.             new Point(43.28388, 95.87901),
  347.             new Point(42.73499, 96.38206),
  348.             new Point(42.79583, 97.1654),
  349.             new Point(42.57194, 99.51012),
  350.             new Point(42.67707, 100.8425),
  351.             new Point(42.50972, 101.8147),
  352.             new Point(42.23333, 102.0772),
  353.             new Point(41.88721, 103.4164),
  354.             new Point(41.87721, 104.5267),
  355.             new Point(41.67068, 104.5237),
  356.             new Point(41.58666, 105.0065),
  357.             new Point(42.46624, 107.4758),
  358.             new Point(42.42999, 109.3107),
  359.             new Point(42.64576, 110.1064),
  360.             new Point(43.31694, 110.9897),
  361.             new Point(43.69221, 111.9583),
  362.             new Point(44.37527, 111.4214),
  363.             new Point(45.04944, 111.873),
  364.             new Point(45.08055, 112.4272),
  365.             new Point(44.8461, 112.853),
  366.             new Point(44.74527, 113.638),
  367.             new Point(45.38943, 114.5453),
  368.             new Point(45.4586, 115.7019),
  369.             new Point(45.72193, 116.2104),
  370.             new Point(46.29583, 116.5855),
  371.             new Point(46.41888, 117.3755),
  372.             new Point(46.57069, 117.425),
  373.             new Point(46.53645, 117.8455),
  374.             new Point(46.73638, 118.3147),
  375.             new Point(46.59895, 119.7068),
  376.             new Point(46.71513, 119.9315),
  377.             new Point(46.90221, 119.9225),
  378.             new Point(47.66499, 119.125),
  379.             new Point(47.99475, 118.5393),
  380.             new Point(48.01125, 117.8046),
  381.             new Point(47.65741, 117.3827),
  382.             new Point(47.88805, 116.8747),
  383.             new Point(47.87819, 116.2624),
  384.             new Point(47.69186, 115.9231),
  385.             new Point(47.91749, 115.5944),
  386.             new Point(48.14353, 115.5491),
  387.             new Point(48.25249, 115.8358),
  388.             new Point(48.52055, 115.8111),
  389.             new Point(49.83047, 116.7114),
  390.             new Point(49.52058, 117.8747),
  391.             new Point(49.92263, 118.5746),
  392.             new Point(50.09631, 119.321),
  393.             new Point(50.33028, 119.36),
  394.             new Point(50.39027, 119.1386),
  395.             new Point(51.62083, 120.0641),
  396.             new Point(52.115, 120.7767),
  397.             new Point(52.34423, 120.6259),
  398.             new Point(52.54267, 120.7122),
  399.             new Point(52.58805, 120.0819),
  400.             new Point(52.76819, 120.0314),
  401.             new Point(53.26374, 120.8307),
  402.             new Point(53.54361, 123.6147),
  403.             new Point(53.18832, 124.4933),
  404.             new Point(53.05027, 125.62),
  405.             new Point(52.8752, 125.6573),
  406.             new Point(52.75722, 126.0968),
  407.             new Point(52.5761, 125.9943),
  408.             new Point(52.12694, 126.555),
  409.             new Point(51.99437, 126.4412),
  410.             new Point(51.38138, 126.9139),
  411.             new Point(51.26555, 126.8176),
  412.             new Point(51.31923, 126.9689),
  413.             new Point(51.05825, 126.9331),
  414.             new Point(50.74138, 127.2919),
  415.             new Point(50.31472, 127.334),
  416.             new Point(50.20856, 127.5861),
  417.             new Point(49.80588, 127.515),
  418.             new Point(49.58665, 127.838),
  419.             new Point(49.58443, 128.7119),
  420.             new Point(49.34676, 129.1118),
  421.             new Point(49.4158, 129.4902),
  422.             new Point(48.86464, 130.2246),
  423.             new Point(48.86041, 130.674),
  424.             new Point(48.60576, 130.5236),
  425.             new Point(48.3268, 130.824),
  426.             new Point(48.10839, 130.6598),
  427.             new Point(47.68721, 130.9922),
  428.             new Point(47.71027, 132.5211),
  429.             new Point(48.09888, 133.0827),
  430.             new Point(48.06888, 133.4843),
  431.             new Point(48.39112, 134.4153),
  432.             new Point(48.26713, 134.7408),
  433.             new Point(47.99207, 134.5576),
  434.             new Point(47.70027, 134.7608),
  435.             new Point(47.32333, 134.1825),
  436.             new Point(46.64017, 133.9977),
  437.             new Point(46.47888, 133.8472),
  438.             new Point(46.25363, 133.9016),
  439.             new Point(45.82347, 133.4761),
  440.             new Point(45.62458, 133.4702),
  441.             new Point(45.45083, 133.1491),
  442.             new Point(45.05694, 133.0253),
  443.             new Point(45.34582, 131.8684),
  444.             new Point(44.97388, 131.4691),
  445.             new Point(44.83649, 130.953),
  446.             new Point(44.05193, 131.298),
  447.             new Point(43.53624, 131.1912),
  448.             new Point(43.38958, 131.3104),
  449.             new Point(42.91645, 131.1285),
  450.             new Point(42.74485, 130.4327),
  451.             new Point(42.42186, 130.6044),
  452.             new Point(42.71416, 130.2468),
  453.             new Point(42.88794, 130.2514),
  454.             new Point(43.00457, 129.9046),
  455.             new Point(42.43582, 129.6955),
  456.             new Point(42.44624, 129.3493),
  457.             new Point(42.02736, 128.9269),
  458.             new Point(42.00124, 128.0566),
  459.             new Point(41.58284, 128.3002),
  460.             new Point(41.38124, 128.1529),
  461.             new Point(41.47249, 127.2708),
  462.             new Point(41.79222, 126.9047),
  463.             new Point(41.61176, 126.5661),
  464.             new Point(40.89694, 126.0118),
  465.             new Point(40.47037, 124.8851),
  466.             new Point(40.09362, 124.3736),
  467.             new Point(39.82777, 124.128),
  468.             new Point(39.8143, 123.2422),
  469.             new Point(39.67388, 123.2167),
  470.             new Point(38.99638, 121.648),
  471.             new Point(38.8611, 121.6982),
  472.             new Point(38.71909, 121.1873),
  473.             new Point(38.91221, 121.0887),
  474.             new Point(39.09013, 121.6794),
  475.             new Point(39.2186, 121.5994),
  476.             new Point(39.35166, 121.7511),
  477.             new Point(39.52847, 121.2283),
  478.             new Point(39.62322, 121.533),
  479.             new Point(39.81138, 121.4683),
  480.             new Point(40.00305, 121.881),
  481.             new Point(40.50562, 122.2987),
  482.             new Point(40.73874, 122.0521),
  483.             new Point(40.92194, 121.1775),
  484.             new Point(40.1961, 120.4468),
  485.             new Point(39.87242, 119.5264),
  486.             new Point(39.15693, 118.9715),
  487.             new Point(39.04083, 118.3273),
  488.             new Point(39.19846, 117.889),
  489.             new Point(38.67555, 117.5364),
  490.             new Point(38.38666, 117.6722),
  491.             new Point(38.16721, 118.0281),
  492.             new Point(38.1529, 118.8378),
  493.             new Point(37.87832, 119.0355),
  494.             new Point(37.30054, 118.9566),
  495.             new Point(37.14361, 119.2328),
  496.             new Point(37.15138, 119.7672),
  497.             new Point(37.35228, 119.8529),
  498.             new Point(37.83499, 120.7371),
  499.             new Point(37.42458, 121.58),
  500.             new Point(37.55256, 122.1282),
  501.             new Point(37.41833, 122.1814),
  502.             new Point(37.39624, 122.5586),
  503.             new Point(37.20999, 122.5972),
  504.             new Point(37.02583, 122.4005),
  505.             new Point(37.01978, 122.5392),
  506.             new Point(36.89361, 122.5047),
  507.             new Point(36.84298, 122.1923),
  508.             new Point(37.00027, 121.9566),
  509.             new Point(36.75889, 121.5944),
  510.             new Point(36.61666, 120.7764),
  511.             new Point(36.52638, 120.96),
  512.             new Point(36.37582, 120.8753),
  513.             new Point(36.42277, 120.7062),
  514.             new Point(36.14075, 120.6956),
  515.             new Point(36.0419, 120.3436),
  516.             new Point(36.26345, 120.3078),
  517.             new Point(36.19998, 120.0889),
  518.             new Point(35.95943, 120.2378),
  519.             new Point(35.57893, 119.6475),
  520.             new Point(34.88499, 119.1761),
  521.             new Point(34.31145, 120.2487),
  522.             new Point(32.97499, 120.8858),
  523.             new Point(32.63889, 120.8375),
  524.             new Point(32.42958, 121.3348),
  525.             new Point(32.11333, 121.4412),
  526.             new Point(32.02166, 121.7066),
  527.             new Point(31.67833, 121.8275),
  528.             new Point(31.86639, 120.9444),
  529.             new Point(32.09361, 120.6019),
  530.             new Point(31.94555, 120.099),
  531.             new Point(32.30638, 119.8267),
  532.             new Point(32.26277, 119.6317),
  533.             new Point(31.90388, 120.1364),
  534.             new Point(31.98833, 120.7026),
  535.             new Point(31.81944, 120.7196),
  536.             new Point(31.30889, 121.6681),
  537.             new Point(30.97986, 121.8828),
  538.             new Point(30.85305, 121.8469),
  539.             new Point(30.56889, 120.9915),
  540.             new Point(30.33555, 120.8144),
  541.             new Point(30.39298, 120.4586),
  542.             new Point(30.19694, 120.15),
  543.             new Point(30.31027, 120.5082),
  544.             new Point(30.06465, 120.7916),
  545.             new Point(30.30458, 121.2808),
  546.             new Point(29.96305, 121.6778),
  547.             new Point(29.88211, 122.1196),
  548.             new Point(29.51167, 121.4483),
  549.             new Point(29.58916, 121.9744),
  550.             new Point(29.19527, 121.9336),
  551.             new Point(29.18388, 121.8119),
  552.             new Point(29.37236, 121.7969),
  553.             new Point(29.19729, 121.7444),
  554.             new Point(29.29111, 121.5611),
  555.             new Point(29.1634, 121.4135),
  556.             new Point(29.02194, 121.6914),
  557.             new Point(28.9359, 121.4908),
  558.             new Point(28.72798, 121.6113),
  559.             new Point(28.84215, 121.1464),
  560.             new Point(28.66993, 121.4844),
  561.             new Point(28.34722, 121.6417),
  562.             new Point(28.13889, 121.3419),
  563.             new Point(28.38277, 121.1651),
  564.             new Point(27.98222, 120.9353),
  565.             new Point(28.07944, 120.5908),
  566.             new Point(27.87229, 120.84),
  567.             new Point(27.59319, 120.5812),
  568.             new Point(27.45083, 120.6655),
  569.             new Point(27.20777, 120.5075),
  570.             new Point(27.28278, 120.1896),
  571.             new Point(27.14764, 120.4211),
  572.             new Point(26.89805, 120.0332),
  573.             new Point(26.64465, 120.128),
  574.             new Point(26.51778, 119.8603),
  575.             new Point(26.78823, 120.0733),
  576.             new Point(26.64888, 119.8668),
  577.             new Point(26.79611, 119.7879),
  578.             new Point(26.75625, 119.5503),
  579.             new Point(26.44222, 119.8204),
  580.             new Point(26.47388, 119.5775),
  581.             new Point(26.33861, 119.658),
  582.             new Point(26.36777, 119.9489),
  583.             new Point(25.99694, 119.4253),
  584.             new Point(26.14041, 119.0975),
  585.             new Point(25.93788, 119.354),
  586.             new Point(25.99069, 119.7058),
  587.             new Point(25.67996, 119.5807),
  588.             new Point(25.68222, 119.4522),
  589.             new Point(25.35333, 119.6454),
  590.             new Point(25.60649, 119.3149),
  591.             new Point(25.42097, 119.1053),
  592.             new Point(25.25319, 119.3526),
  593.             new Point(25.17208, 119.2726),
  594.             new Point(25.2426, 118.8749),
  595.             new Point(24.97194, 118.9866),
  596.             new Point(24.88291, 118.5729),
  597.             new Point(24.75673, 118.7631),
  598.             new Point(24.52861, 118.5953),
  599.             new Point(24.53638, 118.2397),
  600.             new Point(24.68194, 118.1688),
  601.             new Point(24.44024, 118.0199),
  602.             new Point(24.46019, 117.7947),
  603.             new Point(24.25875, 118.1237),
  604.             new Point(23.62437, 117.1957),
  605.             new Point(23.65919, 116.9179),
  606.             new Point(23.355, 116.7603),
  607.             new Point(23.42024, 116.5322),
  608.             new Point(23.23666, 116.7871),
  609.             new Point(23.21083, 116.5139),
  610.             new Point(22.93902, 116.4817),
  611.             new Point(22.73916, 115.7978),
  612.             new Point(22.88416, 115.6403),
  613.             new Point(22.65889, 115.5367),
  614.             new Point(22.80833, 115.1614),
  615.             new Point(22.70277, 114.8889),
  616.             new Point(22.53305, 114.8722),
  617.             new Point(22.64027, 114.718),
  618.             new Point(22.81402, 114.7782),
  619.             new Point(22.69972, 114.5208),
  620.             new Point(22.50423, 114.6136),
  621.             new Point(22.55004, 114.2223),
  622.             new Point(22.42993, 114.3885),
  623.             new Point(22.26056, 114.2961),
  624.             new Point(22.36736, 113.9056),
  625.             new Point(22.50874, 114.0337),
  626.             new Point(22.47444, 113.8608),
  627.             new Point(22.83458, 113.606),
  628.             new Point(23.05027, 113.5253),
  629.             new Point(23.11724, 113.8219),
  630.             new Point(23.05083, 113.4793),
  631.             new Point(22.87986, 113.3629),
  632.             new Point(22.54944, 113.5648),
  633.             new Point(22.18701, 113.5527),
  634.             new Point(22.56701, 113.1687),
  635.             new Point(22.17965, 113.3868),
  636.             new Point(22.04069, 113.2226),
  637.             new Point(22.20485, 113.0848),
  638.             new Point(21.8693, 112.94),
  639.             new Point(21.96472, 112.824),
  640.             new Point(21.70139, 112.2819),
  641.             new Point(21.91611, 111.8921),
  642.             new Point(21.75139, 111.9669),
  643.             new Point(21.77819, 111.6762),
  644.             new Point(21.61264, 111.7832),
  645.             new Point(21.5268, 111.644),
  646.             new Point(21.52528, 111.0285),
  647.             new Point(21.21138, 110.5328),
  648.             new Point(21.37322, 110.3944),
  649.             new Point(20.84381, 110.1594),
  650.             new Point(20.84083, 110.3755),
  651.             new Point(20.64, 110.3239),
  652.             new Point(20.48618, 110.5274),
  653.             new Point(20.24611, 110.2789),
  654.             new Point(20.2336, 109.9244),
  655.             new Point(20.4318, 110.0069),
  656.             new Point(20.92416, 109.6629),
  657.             new Point(21.44694, 109.9411),
  658.             new Point(21.50569, 109.6605),
  659.             new Point(21.72333, 109.5733),
  660.             new Point(21.49499, 109.5344),
  661.             new Point(21.39666, 109.1428),
  662.             new Point(21.58305, 109.1375),
  663.             new Point(21.61611, 108.911),
  664.             new Point(21.79889, 108.8702),
  665.             new Point(21.59888, 108.7403),
  666.             new Point(21.93562, 108.4692),
  667.             new Point(21.59014, 108.5125),
  668.             new Point(21.68999, 108.3336),
  669.             new Point(21.51444, 108.2447),
  670.             new Point(21.54241, 107.99),
  671.             new Point(21.66694, 107.7831),
  672.             new Point(21.60526, 107.3627),
  673.             new Point(22.03083, 106.6933),
  674.             new Point(22.45682, 106.5517),
  675.             new Point(22.76389, 106.7875),
  676.             new Point(22.86694, 106.7029),
  677.             new Point(22.91253, 105.8771),
  678.             new Point(23.32416, 105.3587),
  679.             new Point(23.18027, 104.9075),
  680.             new Point(22.81805, 104.7319),
  681.             new Point(22.6875, 104.3747),
  682.             new Point(22.79812, 104.1113),
  683.             new Point(22.50387, 103.9687),
  684.             new Point(22.78287, 103.6538),
  685.             new Point(22.58436, 103.5224),
  686.             new Point(22.79451, 103.3337),
  687.             new Point(22.43652, 103.0304),
  688.             new Point(22.77187, 102.4744),
  689.             new Point(22.39629, 102.1407),
  690.             new Point(22.49777, 101.7415),
  691.             new Point(22.20916, 101.5744),
  692.             new Point(21.83444, 101.7653),
  693.             new Point(21.14451, 101.786),
  694.             new Point(21.17687, 101.2919),
  695.             new Point(21.57264, 101.1482),
  696.             new Point(21.76903, 101.099),
  697.             new Point(21.47694, 100.6397),
  698.             new Point(21.43546, 100.2057),
  699.             new Point(21.72555, 99.97763),
  700.             new Point(22.05018, 99.95741),
  701.             new Point(22.15592, 99.16785),
  702.             new Point(22.93659, 99.56484),
  703.             new Point(23.08204, 99.5113),
  704.             new Point(23.18916, 98.92747),
  705.             new Point(23.97076, 98.67991),
  706.             new Point(24.16007, 98.89073),
  707.             new Point(23.92999, 97.54762),
  708.             new Point(24.26055, 97.7593),
  709.             new Point(24.47666, 97.54305),
  710.             new Point(24.73992, 97.55255),
  711.             new Point(25.61527, 98.19109),
  712.             new Point(25.56944, 98.36137),
  713.             new Point(25.85597, 98.7104),
  714.             new Point(26.12527, 98.56944),
  715.             new Point(26.18472, 98.73109),
  716.             new Point(26.79166, 98.77777),
  717.             new Point(27.52972, 98.69699),
  718.             new Point(27.6725, 98.45888),
  719.             new Point(27.54014, 98.31992),
  720.             new Point(28.14889, 98.14499),
  721.             new Point(28.54652, 97.55887),
  722.             new Point(28.22277, 97.34888),
  723.             new Point(28.46749, 96.65387),
  724.             new Point(28.35111, 96.40193),
  725.             new Point(28.525, 96.34027),
  726.             new Point(28.79569, 96.61373),
  727.             new Point(29.05666, 96.47083),
  728.             new Point(28.90138, 96.17532),
  729.             new Point(29.05972, 96.14888),
  730.             new Point(29.25757, 96.39172),
  731.             new Point(29.46444, 96.08315),
  732.             new Point(29.03527, 95.38777),
  733.             new Point(29.33346, 94.64751),
  734.             new Point(29.07348, 94.23456),
  735.             new Point(28.6692, 93.96172),
  736.             new Point(28.61876, 93.35194),
  737.             new Point(28.3193, 93.22205),
  738.             new Point(28.1419, 92.71044),
  739.             new Point(27.86194, 92.54498),
  740.             new Point(27.76472, 91.65776),
  741.             new Point(27.945, 91.66277),
  742.             new Point(28.08111, 91.30138),
  743.             new Point(27.96999, 91.08693),
  744.             new Point(28.07958, 90.3765),
  745.             new Point(28.24257, 90.38898),
  746.             new Point(28.32369, 89.99819),
  747.             new Point(28.05777, 89.48749),
  748.             new Point(27.32083, 88.91693)
  749.     };
  750.  
  751.     //Taiwan
  752.     private static final Point[] TAIWAN = new Point[]{
  753.             new Point(25.13474, 121.4441),
  754.             new Point(25.28361, 121.5632),
  755.             new Point(25.00722, 122.0004),
  756.             new Point(24.85028, 121.8182),
  757.             new Point(24.47638, 121.8397),
  758.             new Point(23.0875, 121.3556),
  759.             new Point(21.92791, 120.7196),
  760.             new Point(22.31277, 120.6103),
  761.             new Point(22.54044, 120.3071),
  762.             new Point(23.04437, 120.0539),
  763.             new Point(23.61708, 120.1112),
  764.             new Point(25.00166, 121.0017),
  765.             new Point(25.13474, 121.4441)
  766.     };
  767.  
  768.     //Hainan
  769.     private static final Point[] HAINAN = new Point[]{
  770.             new Point(19.52888, 110.855),
  771.             new Point(19.16761, 110.4832),
  772.             new Point(18.80083, 110.5255),
  773.             new Point(18.3852, 110.0503),
  774.             new Point(18.39152, 109.7594),
  775.             new Point(18.19777, 109.7036),
  776.             new Point(18.50562, 108.6871),
  777.             new Point(19.28028, 108.6283),
  778.             new Point(19.76, 109.2939),
  779.             new Point(19.7236, 109.1653),
  780.             new Point(19.89972, 109.2572),
  781.             new Point(19.82861, 109.4658),
  782.             new Point(19.99389, 109.6108),
  783.             new Point(20.13361, 110.6655),
  784.             new Point(19.97861, 110.9425),
  785.             new Point(19.63829, 111.0215),
  786.             new Point(19.52888, 110.855)
  787.     };
  788.  
  789.     //Chongming
  790.     private static final Point[] CHONGMING = new Point[]{
  791.             new Point(31.80054, 121.2039),
  792.             new Point(31.49972, 121.8736),
  793.             new Point(31.53111, 121.5464),
  794.             new Point(31.80054, 121.2039)
  795.     };
  796.     //endregion
  797.  
  798.     /**
  799.      * 中国行政边界的WGS84坐标数据,
  800.      * 光线投射算法 (Ray casting algorithm) 获得,
  801.      * 沿海、国界周边地区可能会有误差,更高精度需要调整坐标点
  802.      */
  803.     private static final List<Point[]> CHINA_POLYGON = new ArrayList<>();
  804.  
  805.     static {
  806.         CHINA_POLYGON.add(MAINLAND);
  807.         CHINA_POLYGON.add(TAIWAN);
  808.         CHINA_POLYGON.add(HAINAN);
  809.         CHINA_POLYGON.add(CHONGMING);
  810.     }
  811. }

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周三 7月 03, 2024 10:19 pm

Position.java
  1. /*
  2.  * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *     http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.traccar.model;
  17.  
  18. import java.util.Date;
  19. import java.util.List;
  20. import java.util.stream.Collectors;
  21.  
  22. import com.fasterxml.jackson.annotation.JsonIgnore;
  23. import org.traccar.storage.QueryIgnore;
  24. import org.traccar.storage.StorageName;
  25. import org.slf4j.Logger;
  26. import org.slf4j.LoggerFactory;
  27.  
  28. @StorageName("tc_positions")
  29. public class Position extends Message {
  30.  
  31.     public static final String KEY_ORIGINAL = "raw";
  32.     public static final String KEY_INDEX = "index";
  33.     public static final String KEY_HDOP = "hdop";
  34.     public static final String KEY_VDOP = "vdop";
  35.     public static final String KEY_PDOP = "pdop";
  36.     public static final String KEY_SATELLITES = "sat"; // in use
  37.     public static final String KEY_SATELLITES_VISIBLE = "satVisible";
  38.     public static final String KEY_RSSI = "rssi";
  39.     public static final String KEY_GPS = "gps";
  40.     public static final String KEY_ROAMING = "roaming";
  41.     public static final String KEY_EVENT = "event";
  42.     public static final String KEY_ALARM = "alarm";
  43.     public static final String KEY_STATUS = "status";
  44.     public static final String KEY_ODOMETER = "odometer"; // meters
  45.     public static final String KEY_ODOMETER_SERVICE = "serviceOdometer"; // meters
  46.     public static final String KEY_ODOMETER_TRIP = "tripOdometer"; // meters
  47.     public static final String KEY_HOURS = "hours"; // milliseconds
  48.     public static final String KEY_STEPS = "steps";
  49.     public static final String KEY_HEART_RATE = "heartRate";
  50.     public static final String KEY_INPUT = "input";
  51.     public static final String KEY_OUTPUT = "output";
  52.     public static final String KEY_IMAGE = "image";
  53.     public static final String KEY_VIDEO = "video";
  54.     public static final String KEY_AUDIO = "audio";
  55.  
  56.     // The units for the below four KEYs currently vary.
  57.     // The preferred units of measure are specified in the comment for each.
  58.     public static final String KEY_POWER = "power"; // volts
  59.     public static final String KEY_BATTERY = "battery"; // volts
  60.     public static final String KEY_BATTERY_LEVEL = "batteryLevel"; // percentage
  61.     public static final String KEY_FUEL_LEVEL = "fuel"; // liters
  62.     public static final String KEY_FUEL_USED = "fuelUsed"; // liters
  63.     public static final String KEY_FUEL_CONSUMPTION = "fuelConsumption"; // liters/hour
  64.  
  65.     public static final String KEY_VERSION_FW = "versionFw";
  66.     public static final String KEY_VERSION_HW = "versionHw";
  67.     public static final String KEY_TYPE = "type";
  68.     public static final String KEY_IGNITION = "ignition";
  69.     public static final String KEY_FLAGS = "flags";
  70.     public static final String KEY_ANTENNA = "antenna";
  71.     public static final String KEY_CHARGE = "charge";
  72.     public static final String KEY_IP = "ip";
  73.     public static final String KEY_ARCHIVE = "archive";
  74.     public static final String KEY_DISTANCE = "distance"; // meters
  75.     public static final String KEY_TOTAL_DISTANCE = "totalDistance"; // meters
  76.     public static final String KEY_RPM = "rpm";
  77.     public static final String KEY_VIN = "vin";
  78.     public static final String KEY_APPROXIMATE = "approximate";
  79.     public static final String KEY_THROTTLE = "throttle";
  80.     public static final String KEY_MOTION = "motion";
  81.     public static final String KEY_ARMED = "armed";
  82.     public static final String KEY_GEOFENCE = "geofence";
  83.     public static final String KEY_ACCELERATION = "acceleration";
  84.     public static final String KEY_DEVICE_TEMP = "deviceTemp"; // celsius
  85.     public static final String KEY_COOLANT_TEMP = "coolantTemp"; // celsius
  86.     public static final String KEY_ENGINE_LOAD = "engineLoad";
  87.     public static final String KEY_OPERATOR = "operator";
  88.     public static final String KEY_COMMAND = "command";
  89.     public static final String KEY_BLOCKED = "blocked";
  90.     public static final String KEY_LOCK = "lock";
  91.     public static final String KEY_DOOR = "door";
  92.     public static final String KEY_AXLE_WEIGHT = "axleWeight";
  93.     public static final String KEY_G_SENSOR = "gSensor";
  94.     public static final String KEY_ICCID = "iccid";
  95.     public static final String KEY_PHONE = "phone";
  96.     public static final String KEY_SPEED_LIMIT = "speedLimit";
  97.     public static final String KEY_DRIVING_TIME = "drivingTime";
  98.  
  99.     public static final String KEY_DTCS = "dtcs";
  100.     public static final String KEY_OBD_SPEED = "obdSpeed"; // km/h
  101.     public static final String KEY_OBD_ODOMETER = "obdOdometer"; // meters
  102.  
  103.     public static final String KEY_RESULT = "result";
  104.  
  105.     public static final String KEY_DRIVER_UNIQUE_ID = "driverUniqueId";
  106.     public static final String KEY_CARD = "card";
  107.  
  108.     // Start with 1 not 0
  109.     public static final String PREFIX_TEMP = "temp";
  110.     public static final String PREFIX_ADC = "adc";
  111.     public static final String PREFIX_IO = "io";
  112.     public static final String PREFIX_COUNT = "count";
  113.     public static final String PREFIX_IN = "in";
  114.     public static final String PREFIX_OUT = "out";
  115.  
  116.     public static final String ALARM_GENERAL = "general";
  117.     public static final String ALARM_SOS = "sos";
  118.     public static final String ALARM_VIBRATION = "vibration";
  119.     public static final String ALARM_MOVEMENT = "movement";
  120.     public static final String ALARM_LOW_SPEED = "lowspeed";
  121.     public static final String ALARM_OVERSPEED = "overspeed";
  122.     public static final String ALARM_FALL_DOWN = "fallDown";
  123.     public static final String ALARM_LOW_POWER = "lowPower";
  124.     public static final String ALARM_LOW_BATTERY = "lowBattery";
  125.     public static final String ALARM_FAULT = "fault";
  126.     public static final String ALARM_POWER_OFF = "powerOff";
  127.     public static final String ALARM_POWER_ON = "powerOn";
  128.     public static final String ALARM_DOOR = "door";
  129.     public static final String ALARM_LOCK = "lock";
  130.     public static final String ALARM_UNLOCK = "unlock";
  131.     public static final String ALARM_GEOFENCE = "geofence";
  132.     public static final String ALARM_GEOFENCE_ENTER = "geofenceEnter";
  133.     public static final String ALARM_GEOFENCE_EXIT = "geofenceExit";
  134.     public static final String ALARM_GPS_ANTENNA_CUT = "gpsAntennaCut";
  135.     public static final String ALARM_ACCIDENT = "accident";
  136.     public static final String ALARM_TOW = "tow";
  137.     public static final String ALARM_IDLE = "idle";
  138.     public static final String ALARM_HIGH_RPM = "highRpm";
  139.     public static final String ALARM_ACCELERATION = "hardAcceleration";
  140.     public static final String ALARM_BRAKING = "hardBraking";
  141.     public static final String ALARM_CORNERING = "hardCornering";
  142.     public static final String ALARM_LANE_CHANGE = "laneChange";
  143.     public static final String ALARM_FATIGUE_DRIVING = "fatigueDriving";
  144.     public static final String ALARM_POWER_CUT = "powerCut";
  145.     public static final String ALARM_POWER_RESTORED = "powerRestored";
  146.     public static final String ALARM_JAMMING = "jamming";
  147.     public static final String ALARM_TEMPERATURE = "temperature";
  148.     public static final String ALARM_PARKING = "parking";
  149.     public static final String ALARM_BONNET = "bonnet";
  150.     public static final String ALARM_FOOT_BRAKE = "footBrake";
  151.     public static final String ALARM_FUEL_LEAK = "fuelLeak";
  152.     public static final String ALARM_TAMPERING = "tampering";
  153.     public static final String ALARM_REMOVING = "removing";
  154.  
  155.     public Position() {
  156.     }
  157.  
  158.     public Position(String protocol) {
  159.         this.protocol = protocol;
  160.     }
  161.  
  162.     private String protocol;
  163.  
  164.     public String getProtocol() {
  165.         return protocol;
  166.     }
  167.  
  168.     public void setProtocol(String protocol) {
  169.         this.protocol = protocol;
  170.     }
  171.  
  172.     private Date serverTime = new Date();
  173.  
  174.     public Date getServerTime() {
  175.         return serverTime;
  176.     }
  177.  
  178.     public void setServerTime(Date serverTime) {
  179.         this.serverTime = serverTime;
  180.     }
  181.  
  182.     private Date deviceTime;
  183.  
  184.     public Date getDeviceTime() {
  185.         return deviceTime;
  186.     }
  187.  
  188.     public void setDeviceTime(Date deviceTime) {
  189.         this.deviceTime = deviceTime;
  190.     }
  191.  
  192.     private Date fixTime;
  193.  
  194.     public Date getFixTime() {
  195.         return fixTime;
  196.     }
  197.  
  198.     public void setFixTime(Date fixTime) {
  199.         this.fixTime = fixTime;
  200.     }
  201.  
  202.     @QueryIgnore
  203.     public void setTime(Date time) {
  204.         setDeviceTime(time);
  205.         setFixTime(time);
  206.     }
  207.  
  208.     private boolean outdated;
  209.  
  210.     @QueryIgnore
  211.     public boolean getOutdated() {
  212.         return outdated;
  213.     }
  214.  
  215.     @QueryIgnore
  216.     public void setOutdated(boolean outdated) {
  217.         this.outdated = outdated;
  218.     }
  219.  
  220.     private boolean valid;
  221.     public boolean lat_tag = false, lon_tag = false;
  222.  
  223.     public boolean getValid() {
  224.         return valid;
  225.     }
  226.  
  227.     public void setValid(boolean valid) {
  228.         this.valid = valid;
  229.     }
  230.  
  231.     private double latitude, lat_temp;
  232.  
  233.     public double getLatitude() {
  234.         return latitude;
  235.     }
  236.  
  237.     private static final Logger LOGGER = LoggerFactory.getLogger("Position");
  238.  
  239.     public void setLatitude(double latitude) {
  240.         if (latitude < -90 || latitude > 90) {
  241.             throw new IllegalArgumentException("Latitude out of range");
  242.         }
  243.         this.latitude = latitude;
  244.         this.lat_temp = latitude;
  245.         if (lat_tag && lon_tag) {
  246.             double[] pos_gcj02 = GpsCoordinateUtils.calWGS84toGCJ02(latitude, longitude);
  247.             this.latitude = pos_gcj02[0];
  248.             this.longitude = pos_gcj02[1];
  249.             lat_tag = false;
  250.             lon_tag = false;
  251.             LOGGER.info("<Coordinate lat>: WGS84({},{}) => GCJ02({},{})", lat_temp, lon_temp, this.latitude, this.longitude);
  252.         }
  253.     }
  254.  
  255.     private double longitude, lon_temp;
  256.  
  257.     public double getLongitude() {
  258.         return longitude;
  259.     }
  260.  
  261.     public void setLongitude(double longitude) {
  262.         if (longitude < -180 || longitude > 180) {
  263.             throw new IllegalArgumentException("Longitude out of range");
  264.         }
  265.         this.longitude = longitude;
  266.         this.lon_temp = longitude;
  267.         if (lat_tag && lon_tag) {
  268.             double[] pos_gcj02 = GpsCoordinateUtils.calWGS84toGCJ02(latitude, longitude);
  269.             this.latitude = pos_gcj02[0];
  270.             this.longitude = pos_gcj02[1];
  271.             lat_tag = false;
  272.             lon_tag = false;
  273.             LOGGER.info("<Coordinate lat>: WGS84({},{}) => GCJ02({},{})", lat_temp, lon_temp, this.latitude, this.longitude);
  274.         }
  275.     }
  276.  
  277.     private double altitude; // value in meters
  278.  
  279.     public double getAltitude() {
  280.         return altitude;
  281.     }
  282.  
  283.     public void setAltitude(double altitude) {
  284.         this.altitude = altitude;
  285.     }
  286.  
  287.     private double speed; // value in knots
  288.  
  289.     public double getSpeed() {
  290.         return speed;
  291.     }
  292.  
  293.     public void setSpeed(double speed) {
  294.         this.speed = speed;
  295.     }
  296.  
  297.     private double course;
  298.  
  299.     public double getCourse() {
  300.         return course;
  301.     }
  302.  
  303.     public void setCourse(double course) {
  304.         this.course = course;
  305.     }
  306.  
  307.     private String address;
  308.  
  309.     public String getAddress() {
  310.         return address;
  311.     }
  312.  
  313.     public void setAddress(String address) {
  314.         this.address = address;
  315.     }
  316.  
  317.     private double accuracy;
  318.  
  319.     public double getAccuracy() {
  320.         return accuracy;
  321.     }
  322.  
  323.     public void setAccuracy(double accuracy) {
  324.         this.accuracy = accuracy;
  325.     }
  326.  
  327.     private Network network;
  328.  
  329.     public Network getNetwork() {
  330.         return network;
  331.     }
  332.  
  333.     public void setNetwork(Network network) {
  334.         this.network = network;
  335.     }
  336.  
  337.     private List<Long> geofenceIds;
  338.  
  339.     public List<Long> getGeofenceIds() {
  340.         return geofenceIds;
  341.     }
  342.  
  343.     public void setGeofenceIds(List<? extends Number> geofenceIds) {
  344.         if (geofenceIds != null) {
  345.             this.geofenceIds = geofenceIds.stream().map(Number::longValue).collect(Collectors.toList());
  346.         } else {
  347.             this.geofenceIds = null;
  348.         }
  349.     }
  350.  
  351.     @JsonIgnore
  352.     @QueryIgnore
  353.     @Override
  354.     public String getType() {
  355.         return super.getType();
  356.     }
  357.  
  358.     @JsonIgnore
  359.     @QueryIgnore
  360.     @Override
  361.     public void setType(String type) {
  362.         super.setType(type);
  363.     }
  364. }

Re: traccar各设备通讯协议分析(坐标转换、逆地址解析)

BG6RSH » 周二 6月 18, 2024 4:06 pm

# 高德地图逆地址解析函数# 逆地址解析功能监听处理函数,PYTHON代码。
  1. from http.server import BaseHTTPRequestHandler, HTTPServer
  2. import requests
  3. import json
  4. import urllib.parse
  5.  
  6. mapquest_addr = '''
  7. "results": [
  8.    {{
  9.      "providedLocation": {{ }},
  10.      "locations": [
  11.        {{
  12.          "street": "{0}",
  13.          "adminArea5": "{1}",
  14.          "adminArea5Type": "City",
  15.          "adminArea4": "{2}",
  16.          "adminArea4Type": "County",
  17.          "adminArea3": "{3}",
  18.          "adminArea3Type": "State",
  19.          "adminArea1": "{4}",
  20.          "adminArea1Type": "Country",
  21.          "postalCode": "{5}",    
  22.        }}
  23.      ]
  24.    }}
  25.  ]
  26. '''
  27.  
  28. proxies = {
  29.     "http": "http://D45623:zaq1XSW2@172.16.144.232:9128",
  30.     "https": "http://D45623:zaq1XSW2@172.16.144.232:9128",
  31. }
  32. traccar_url = 'http://nas.atoo.top:8082'
  33.  
  34.  
  35. # 高德地图逆地址解析函数
  36. def amap_geocode(lat, lng):
  37.     # http://127.0.0.1/v3/geocode/regeo?output=xml&location=39.991957,116.310003
  38.     amap_url = 'https://restapi.amap.com/v3/geocode/regeo?'
  39.     parameters = {
  40.         'output': 'json',
  41.         'key': 'de6cf58608a044b643da48377c6bdbfb',
  42.         'radius': '1000',
  43.         'xtensions': 'base',
  44.         'location': '{0},{1}'.format(lat, lng)
  45.     }
  46.  
  47.     response = requests.get(amap_url, parameters, proxies=proxies)
  48.     if response.status_code == 200:
  49.         print(response.text)
  50.         return response.text
  51.     else:
  52.         return ''
  53.  
  54.  
  55. # 高德地图坐标转换
  56. def amap_coordinate_convert(lat, lon):
  57.     amap_url = 'https://restapi.amap.com/v3/assistant/coordinate/convert?'
  58.     parameters = {
  59.         'output': 'json',
  60.         'key': 'de6cf58608a044b643da48377c6bdbfb',
  61.         'coordsys': 'gps',
  62.         'locations': '{0},{1}'.format(lon, lat)
  63.     }
  64.     response = requests.get(amap_url, parameters, proxies=proxies)
  65.     # print(lat, lon, requests)
  66.     if response.status_code == 200:
  67.         # print(response.text)
  68.         coordinate_json = json.loads(response.text)
  69.         if coordinate_json['status'] == '1':
  70.             coordinate = coordinate_json['locations'].split(',')
  71.             return coordinate[0], coordinate[1]
  72.         return -1, -1
  73.  
  74.  
  75. # 逆地址解析功能监听处理函数
  76. class GeocodeHandler(BaseHTTPRequestHandler):
  77.     def do_GET(self) -> None:
  78.         request_str = self.requestline
  79.         # print(request_str)
  80.         l = request_str.find('location=')
  81.         r = request_str.rfind(' HTTP')
  82.         if l != -1 and r != -1:
  83.             request_str = request_str[l + 9:r]
  84.             point = request_str.split(',')
  85.             # print(point[1],point[0])
  86.             address_json = json.loads(amap_geocode(point[1], point[0]))
  87.             address = mapquest_addr.format(address_json['regeocode']['formatted_address'],
  88.                                            address_json['regeocode']['addressComponent']['city'],
  89.                                            address_json['regeocode']['addressComponent']['district'],
  90.                                            address_json['regeocode']['addressComponent']['province'],
  91.                                            address_json['regeocode']['addressComponent']['country'],
  92.                                            address_json['regeocode']['addressComponent']['adcode']
  93.                                            )
  94.             print(address)
  95.         else:
  96.             address = ''
  97.         self.send_response(200)
  98.         self.send_header('Content-type', 'text/html;charset=utf-8')
  99.         self.end_headers()
  100.         self.wfile.write(bytes(address, 'utf-8'))
  101.  
  102.  
  103. class OsmAndProtocolHandler(BaseHTTPRequestHandler):
  104.     def do_GET(self):
  105.         # content_length = int(self.headers['Content-Length'])
  106.         # body = self.rfile.read(content_length)
  107.         # data = json.loads(body.decode('utf-8'))
  108.         request_str = self.requestline
  109.         l = request_str.find('GET /?')
  110.         r = request_str.rfind(' HTTP')
  111.         if l != -1 and r != -1:
  112.             data = urllib.parse.parse_qs(self.requestline[l + 6:r])
  113.             print('Data', data)
  114.             # data = json.loads(self.requestline)
  115.             lat, lon = amap_coordinate_convert(data['lat'][0], data['lon'][0])
  116.             parameters = {
  117.                 'id': data['id'][0],
  118.                 'timestamp': data['timestamp'][0],
  119.                 'lat': lat,
  120.                 'lon': lon,
  121.                 'speed': data['speed'][0],
  122.                 'bearing': data['bearing'][0],
  123.                 'altitude': data['altitude'][0],
  124.                 'accuracy': data['accuracy'][0],
  125.                 'batt': data['batt'][0]
  126.             }
  127.             response = requests.post(traccar_url, parameters, proxies=proxies)
  128.             print('parameters', parameters)
  129.             self.send_response(200)
  130.             self.send_header('Content-type', 'text/html;charset=utf-8')
  131.             self.end_headers()
  132.             self.wfile.write(bytes(json.dumps(parameters), 'utf-8'))
  133.  
  134.  
  135. def run(server_address, handler_class=BaseHTTPRequestHandler):
  136.     # server_address = ('', 8000)
  137.     httpd = HTTPServer(server_address, handler_class)
  138.     httpd.serve_forever()
  139.  
  140.  
  141. if __name__ == '__main__':
  142.     print('traccar坐标转换代理服务器')
  143.     # run(('', 80), GeocodeHandler)
  144.     run(('', 8082), OsmAndProtocolHandler)

页首