老板想集成地图又不想花钱,于是让我
点击关注公众号,“技术干货”及时达!
前言在数字化时代,地图服务已成为各类应用的标配,无论是导航、位置分享还是商业分析,地图都扮演着不可或缺的角色。然而,高质量的地图服务往往伴随着不菲的授权费用。公司原先使用的是国内某知名地图服务,但随着业务的扩展和成本的考量,老板决定寻找一种成本更低的解决方案。于是,我们的目光转向了免费的地图服务——天地图。
天地图简介天地图(http://lbs.tianditu.gov.cn/server/guide.html)
是中国领先的在线地图服务之一,提供全面的地理信息服务。它的API支持地理编码、逆地理编码、周边搜索等多种功能,且完全免费。这正是我们需要的。
具体实现代码为了将天地图集成到我们的系统中,我们需要进行一系列的开发工作。以下是实现过程中的关键代码段。
逆地理编码逆地理编码是将经纬度转换为可读的地址。在天地图中,这一功能可以通过以下代码实现:
public static MapLocation reverseGeocode(String longitude, String latitude) { Request request = new Request(); LocateInfo locateInfo = GCJ02_WGS84Utils.gcj02_To_Wgs84(Double.valueOf(latitude), Double.valueOf(longitude)); longitude = String.valueOf(locateInfo.getLongitude()); latitude = String.valueOf(locateInfo.getLatitude()); String postStr = String.format(REVERSE_GEOCODE_POST_STR, longitude, latitude); String encodedPostStr = null; try { encodedPostStr = URLEncoder.encode(postStr, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String url = REVERSE_GEOCODE_URL + "?tk=" + TK + "&type=" + GEOCODE + "&postStr=" + encodedPostStr; request.setUrl(url); ResponseString response = HttpClientHelper.getWithoutUserAgent(request, null); if (response.getSuccess()) { String body = response.getBody(); JSONObject jsonObject = JSON.parseObject(body); String status = jsonObject.getString("status"); if (!"0".equals(status)) { return null; } JSONObject resultObject = jsonObject.getJSONObject("result"); MapLocation mapLocation = new MapLocation(); String formattedAddress = resultObject.getString("formatted_address"); mapLocation.setAddress(formattedAddress); String locationStr = resultObject.getString("location"); JSONObject location = JSON.parseObject(locationStr); String lon = location.getString("lon"); String lat = location.getString("lat"); locateInfo = GCJ02_WGS84Utils.wgs84_To_Gcj02(Double.valueOf(lat), Double.valueOf(lon)); lon = String.valueOf(locateInfo.getLongitude()); lat = String.valueOf(locateInfo.getLatitude()); mapLocation.setLongitude(lon); mapLocation.setLatitude(lat); JSONObject addressComponent = resultObject.getJSONObject("addressComponent"); String address = addressComponent.getString("address"); mapLocation.setName(address); mapLocation.setCity(addressComponent.getString("city")); return mapLocation; } return null;}周边搜索周边搜索允许我们根据一个地点的经纬度搜索附近的其他地点。实现代码如下:
public static ListMapLocation nearbySearch(String query, String longitude, String latitude, String radius) { LocateInfo locateInfo = GCJ02_WGS84Utils.gcj02_To_Wgs84(Double.valueOf(latitude), Double.valueOf(longitude)); longitude = String.valueOf(locateInfo.getLongitude()); latitude = String.valueOf(locateInfo.getLatitude()); Request request = new Request(); String longLat = longitude + "," + latitude; String postStr = String.format(NEARBY_SEARCH_POST_STR, query, Integer.valueOf(radius), longLat); String encodedPostStr = null; try { encodedPostStr = URLEncoder.encode(postStr, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String url = SEARCH_URL + "?tk=" + TK + "&type=" + QUERY + "&postStr=" + encodedPostStr; request.setUrl(url); ResponseString response = HttpClientHelper.getWithoutUserAgent(request, null); ListMapLocation list = new ArrayList(); if (response.getSuccess()) { String body = response.getBody(); JSONObject jsonObject = JSON.parseObject(body); JSONObject statusObject = jsonObject.getJSONObject("status"); String infoCode = statusObject.getString("infocode"); if (!"1000".equals(infoCode)) { return new ArrayList(); } String resultType = jsonObject.getString("resultType"); String count = jsonObject.getString("count"); if (!"1".equals(resultType) || "0".equals(count)) { return new ArrayList(); } JSONArray poisArray = jsonObject.getJSONArray("pois"); for (int i = 0; i poisArray.size(); i++) { JSONObject poiObject = poisArray.getJSONObject(i); MapLocation mapLocation = new MapLocation(); mapLocation.setName(poiObject.getString("name")); mapLocation.setAddress(poiObject.getString("address")); String lonlat = poiObject.getString("lonlat"); String[] lonlatArr = lonlat.split(","); locateInfo = GCJ02_WGS84Utils.wgs84_To_Gcj02(Double.valueOf(lonlatArr[1]), Double.valueOf(lonlatArr[0])); String lon = String.valueOf(locateInfo.getLongitude()); String lat = String.valueOf(locateInfo.getLatitude()); mapLocation.setLongitude(lon); mapLocation.setLatitude(lat); list.add(mapLocation); } } return list;}文本搜索文本搜索功能允许用户根据关键词搜索地点。实现代码如下:
public static ListMapLocation searchByText(String query, String mapBound) { Request request = new Request(); String postStr = String.format(SEARCH_BY_TEXT_POST_STR, query, mapBound); String encodedPostStr = null; try { encodedPostStr = URLEncoder.encode(postStr, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String url = SEARCH_URL + "?tk=" + TK + "&type=" + QUERY + "&postStr=" + encodedPostStr; request.setUrl(url); ResponseString response = HttpClientHelper.getWithoutUserAgent(request, null); ListMapLocation list = new ArrayList(); if (response.getSuccess()) { String body = response.getBody(); JSONObject jsonObject = JSON.parseObject(body); JSONObject statusObject = jsonObject.getJSONObject("status"); String infoCode = statusObject.getString("infocode"); if (!"1000".equals(infoCode)) { return new ArrayList(); } String resultType = jsonObject.getString("resultType"); String count = jsonObject.getString("count");
if (!"1".equals(resultType) || "0".equals(count)) { return new ArrayList(); } JSONArray poisArray = jsonObject.getJSONArray("pois"); for (int i = 0; i poisArray.size(); i++) { JSONObject poiObject = poisArray.getJSONObject(i); MapLocation mapLocation = new MapLocation(); mapLocation.setName(poiObject.getString("name")); mapLocation.setAddress(poiObject.getString("address")); String lonlat = poiObject.getString("lonlat"); String[] lonlatArr = lonlat.split(","); LocateInfo locateInfo = GCJ02_WGS84Utils.wgs84_To_Gcj02(Double.valueOf(lonlatArr[1]), Double.valueOf(lonlatArr[0])); String lon = String.valueOf(locateInfo.getLongitude()); String lat = String.valueOf(locateInfo.getLatitude()); mapLocation.setLongitude(lon); mapLocation.setLatitude(lat); list.add(mapLocation); } } return list;}坐标系转换由于天地图使用的是WGS84坐标系,而国内常用的是GCJ-02坐标系,因此我们需要进行坐标转换。以下是坐标转换的工具类:
/** * WGS-84:是国际标准,GPS坐标(Google Earth使用、或者GPS模块) * GCJ-02:中国坐标偏移标准,Google Map、高德、腾讯使用 * BD-09:百度坐标偏移标准,Baidu Map使用(经由GCJ-02加密而来) * * 这些坐标系是对真实坐标系统进行人为的加偏处理,按照特殊的算法,将真实的坐标加密成虚假的坐标, * 而这个加偏并不是线性的加偏,所以各地的偏移情况都会有所不同,具体的内部实现是没有对外开放的, * 但是坐标之间的转换算法是对外开放,在网上可以查到的,此算法的误差在0.1-0.4之间。 */public class GCJ02_WGS84Utils {
public static double pi = 3.1415926535897932384626;//圆周率 public static double a = 6378245.0;//克拉索夫斯基椭球参数长半轴a public static double ee = 0.00669342162296594323;//克拉索夫斯基椭球参数第一偏心率平方
/** * 从GPS转高德 * isOutOfChina 方法用于判断经纬度是否在中国范围内,如果不在中国范围内,则直接返回原始的WGS-84坐标。 * transformLat 和 transformLon 是辅助函数,用于进行经纬度的转换计算。 * 最终,wgs84ToGcj02 方法返回转换后的GCJ-02坐标系下的经纬度。 */ public static LocateInfo wgs84_To_Gcj02(double lat, double lon) { LocateInfo info = new LocateInfo(); if (isOutOfChina(lat, lon)) { info.setChina(false); info.setLatitude(lat); info.setLongitude(lon); } else { double dLat = transformLat(lon - 105.0, lat - 35.0); double dLon = transformLon(lon - 105.0, lat - 35.0); double radLat = lat / 180.0 * pi; double magic = Math.sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); double mgLat = lat + dLat; double mgLon = lon + dLon; info.setChina(true); info.setLatitude(mgLat); info.setLongitude(mgLon); } return info; }
//从高德转到GPS public static LocateInfo gcj02_To_Wgs84(double lat, double lon) { LocateInfo info = new LocateInfo(); LocateInfo gps = transform(lat, lon); double lontitude = lon * 2 - gps.getLongitude(); double latitude = lat * 2 - gps.getLatitude(); info.setChina(gps.isChina()); info.setLatitude(latitude); info.setLongitude(lontitude); return info; }
// 判断坐标是否在国外 private static boolean isOutOfChina(double lat, double lon) { if (lon 72.004 || lon 137.8347) return true; if (lat 0.8293 || lat 55.8271) return true; return false; }
//转换 private static LocateInfo transform(double lat, double lon) { LocateInfo info = new LocateInfo(); if (isOutOfChina(lat, lon)) { info.setChina(false); info.setLatitude(lat); info.setLongitude(lon); return info; } double dLat = transformLat(lon - 105.0, lat - 35.0); double dLon = transformLon(lon - 105.0, lat - 35.0); double radLat = lat / 180.0 * pi; double magic = Math.sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); double mgLat = lat + dLat; double mgLon = lon + dLon; info.setChina(true); info.setLatitude(mgLat); info.setLongitude(mgLon);
return info; }
//转换纬度所需 private static double transformLat(double x, double y) { 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)); ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0; ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0; return ret; }
//转换经度所需 private static double transformLon(double x, double y) { double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0; ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0; return ret; }}结论通过上述代码,我们成功地将天地图集成到了我们的系统中,不仅满足了功能需求,还大幅降低了成本。这一过程中,我们深入理解了地图服务的工作原理,也提升了团队的技术能力。
注意事项确保在使用天地图API时遵守其服务条款,尤其是在商业用途中。由于网络或其他原因,天地图API可能存在访问延迟或不稳定的情况,建议在生产环境中做好异常处理和备用方案。坐标系转换是一个复杂的过程,确保使用可靠的算法和工具进行转换,以保证定位的准确性。通过这次集成,我们不仅为公司节省了成本,还提升了系统的稳定性和用户体验。在未来的开发中,我们将继续探索更多高效、低成本的技术解决方案。
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线