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