分页: 1 / 1

为Traccar添加腾讯地图支持

发表于 : 周三 10月 08, 2025 4:31 pm
BG6RSH
一、Java服务器修改
1、src/main/java/org/traccar/geocoder/JsonGeocoder.java,添加高德逆地址解析支持,它传递经纬度数据与其他服务商相反
  1.     public String getAddress(
  2.             final double latitude, final double longitude, final ReverseGeocoderCallback callback) {
  3.  
  4.         if (cache != null) {
  5.             String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude));
  6.             if (cachedAddress != null) {
  7.                 if (callback != null) {
  8.                     callback.onSuccess(cachedAddress);
  9.                 }
  10.                 return cachedAddress;
  11.             }
  12.         }
  13.  
  14.         if (statisticsManager != null) {
  15.             statisticsManager.registerGeocoderRequest();
  16.         }
  17.  
  18.         Invocation.Builder request;
  19.         if(url.contains("amap.com")) {
  20.             // 高德地图
  21.             request = client.target(String.format(url, longitude, latitude)).request();
  22.         }else {
  23.             // 其他地图
  24.             request = client.target(String.format(url, latitude, longitude)).request();
  25.         }
  26.  
  27.         if (callback != null) {
  28.             request.async().get(new InvocationCallback<JsonObject>() {
  29.                 @Override
  30.                 public void completed(JsonObject json) {
  31.                     handleResponse(latitude, longitude, json, callback);
  32.                 }
  33.  
  34.                 @Override
  35.                 public void failed(Throwable throwable) {
  36.                     callback.onFailure(throwable);
  37.                 }
  38.             });
  39.         } else {
  40.             try {
  41.                 return handleResponse(latitude, longitude, request.get(JsonObject.class), null);
  42.             } catch (Exception e) {
  43.                 LOGGER.warn("Geocoder network error", e);
  44.             }
  45.         }
  46.         return null;
  47.     }
2、src/main/java/org/traccar/MainModule.java,添加高德、腾讯逆地址解码协议支持
  1.     public static Geocoder provideGeocoder(Config config, Client client, StatisticsManager statisticsManager) {
  2.         if (config.getBoolean(Keys.GEOCODER_ENABLE)) {
  3.             String type = config.getString(Keys.GEOCODER_TYPE);
  4.             String url = config.getString(Keys.GEOCODER_URL);
  5.             String key = config.getString(Keys.GEOCODER_KEY);
  6.             String language = config.getString(Keys.GEOCODER_LANGUAGE);
  7.             String formatString = config.getString(Keys.GEOCODER_FORMAT);
  8.             AddressFormat addressFormat = formatString != null ? new AddressFormat(formatString) : new AddressFormat();
  9.  
  10.             int cacheSize = config.getInteger(Keys.GEOCODER_CACHE_SIZE);
  11.             Geocoder geocoder = switch (type) {
  12.                 case "pluscodes" -> new PlusCodesGeocoder();
  13.                 case "amap" -> new AmapGeocoder(client, url, key, cacheSize, addressFormat);// 高德逆地址解码
  14.                 case "qq" -> new QqGeocoder(client, url, key, cacheSize, addressFormat);    // 腾讯逆地址解码
  15.                 case "nominatim" -> new NominatimGeocoder(client, url, key, language, cacheSize, addressFormat);
  16.                 case "locationiq" -> new LocationIqGeocoder(client, url, key, language, cacheSize, addressFormat);
  17.                 case "gisgraphy" -> new GisgraphyGeocoder(client, url, cacheSize, addressFormat);
  18.                 case "mapquest" -> new MapQuestGeocoder(client, url, key, cacheSize, addressFormat);
  19.                 case "opencage" -> new OpenCageGeocoder(client, url, key, language, cacheSize, addressFormat);
  20.                 case "bingmaps" -> new BingMapsGeocoder(client, url, key, cacheSize, addressFormat);
  21.                 case "factual" -> new FactualGeocoder(client, url, key, cacheSize, addressFormat);
  22.                 case "geocodefarm" -> new GeocodeFarmGeocoder(client, key, language, cacheSize, addressFormat);
  23.                 case "geocodexyz" -> new GeocodeXyzGeocoder(client, key, cacheSize, addressFormat);
  24.                 case "ban" -> new BanGeocoder(client, cacheSize, addressFormat);
  25.                 case "here" -> new HereGeocoder(client, url, key, language, cacheSize, addressFormat);
  26.                 case "mapmyindia" -> new MapmyIndiaGeocoder(client, url, key, cacheSize, addressFormat);
  27.                 case "tomtom" -> new TomTomGeocoder(client, url, key, cacheSize, addressFormat);
  28.                 case "positionstack" -> new PositionStackGeocoder(client, key, cacheSize, addressFormat);
  29.                 case "mapbox" -> new MapboxGeocoder(client, key, cacheSize, addressFormat);
  30.                 case "maptiler" -> new MapTilerGeocoder(client, key, cacheSize, addressFormat);
  31.                 case "geoapify" -> new GeoapifyGeocoder(client, key, language, cacheSize, addressFormat);
  32.                 case "geocodejson" -> new GeocodeJsonGeocoder(client, url, key, language, cacheSize, addressFormat);
  33.                 default -> new GoogleGeocoder(client, url, key, language, cacheSize, addressFormat);
  34.             };
  35.             geocoder.setStatisticsManager(statisticsManager);
  36.             return geocoder;
  37.         }
  38.         return null;
  39.     }
3、src/main/java/org/traccar/geocoder/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. }
二、Web程序修改
1、src\resources\l10n\zh.json,添加GCJ02、WGS84坐标名称中文定义
  1.     "positionFixTime": "修正时间",
  2.     "positionDeviceTime": "设备时间",
  3.     "positionServerTime": "服务器时间",
  4.     "positionValid": "有效",
  5.     "positionAccuracy": "精度",
  6.     "positionLatitudeGcj02": "火星坐标纬度",
  7.     "positionLongitudeGcj02": "火星坐标经度",
  8.     "positionLatitudeWgs84": "GPS坐标纬度",
  9.     "positionLongitudeWgs84": "GPS坐标经度",
  10.     "positionAltitude": "海拔",
  11.     "positionSpeed": "速度",
  12.     "positionCourse": "行驶方向",
  13.     "positionAddress": "地址",
  14.     "positionProtocol": "协议",
  15.     "positionDistance": "里程",
2、src\common\attributes\usePositionAttributes.js,添加GCJ02、WGS84坐标名称属性定义
  1.   latitude: {
  2.     name: t('positionLatitudeGcj02'),
  3.     type: 'number',
  4.     property: true,
  5.   },
  6.   longitude: {
  7.     name: t('positionLongitudeGcj02'),
  8.     type: 'number',
  9.     property: true,
  10.   },
  11.   latitudeWgs84: {
  12.     name: t('positionLatitudeWgs84'),
  13.     type: 'number',
  14.     property: true,
  15.   },
  16.   longitudeWgs84: {
  17.     name: t('positionLongitudeWgs84'),
  18.     type: 'number',
  19.     property: true,
  20.   },
3、src\map\core\useMapStyles.js,添加高德、腾讯瓦片地图支持
  1.     {
  2.       id: 'autoNavi',
  3.       title: t('mapAutoNavi'),
  4.       style: styleCustom({
  5.         tiles: [1, 2, 3, 4].map((i) => `https://webrd0${i}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}`),
  6.         minZoom: 3,
  7.         maxZoom: 18,
  8.         attribution: '© 高德地图',
  9.       }),
  10.       available: true,
  11.     },
  12.     {
  13.       id: 'tencentMap',
  14.       title: t('mapTencent'), // 需要在src\resources\l10n\zh.json文件中添加对应翻译
  15.       style: styleCustom({
  16.         tiles: ['tencent://{z}/{x}/{y}'],   // ① 自定义协议解析:src\map\core\MapView.jsx
  17.         minZoom: 3,
  18.         maxZoom: 18,
  19.         attribution: '© 腾讯地图',
  20.       }),
  21.       available: true,
  22.     },
4、src\map\core\MapView.jsx,新增腾讯协议拦截
  1. maplibregl.addProtocol('google', googleProtocol);
  2.  
  3. /* ****** 新增:腾讯协议拦截 ****** */
  4. maplibregl.addProtocol('tencent', async (params) => {
  5.   const seg = params.url.split('/');        
  6.   const z = +seg[2], x = +seg[3], y = +seg[4];
  7.    
  8.   if (z < 0 || x < 0 || y < 0 || y >= (1 << z))  {
  9.     return { data: null };
  10.   }
  11.  
  12.   const yTMS = (1 << z) - 1 - y;
  13.   const host = `rt${(x % 4)}.map.gtimg.com`;
  14.   const realUrl = `https://${host}/realtimerender?z=${z}&x=${x}&y=${yTMS}&style=0`;
  15.  
  16.   console.log('[tencent tile]', realUrl);
  17.  
  18.   return fetch(realUrl)
  19.     .then((res) => (res.ok ? res.arrayBuffer() : Promise.reject()))
  20.     .then((buf) => ({ data: buf }))
  21.     .catch(() => ({ data: null }));
  22. });
  23. /* ****** 新增结束 ****** */
5、src\login\LoginPage.jsx,添加论坛链接
  1.           <div className={classes.extraContainer}>
  2.             {registrationEnabled && (
  3.               <Link
  4.                 onClick={() => navigate('/register')}
  5.                 className={classes.link}
  6.                 underline="none"
  7.                 variant="caption"
  8.               >
  9.                 {t('loginRegister')}
  10.               </Link>
  11.             )}
  12.             {emailEnabled && (
  13.               <Link
  14.                 onClick={() => navigate('/reset-password')}
  15.                 className={classes.link}
  16.                 underline="none"
  17.                 variant="caption"
  18.               >
  19.                 {t('loginReset')}
  20.               </Link>
  21.             )}
  22.             <Link className={classes.link}
  23.               underline="none"
  24.               variant="caption"
  25.               href="http://bbs.atoo.top:8081" target="_blank" rel="noopener noreferrer">
  26.               访问论坛
  27.             </Link>
  28.           </div>
6、index.html,修改网页标题
  1. <title>${title} 火星坐标版</title>