老板想集成地图又不想花钱,于是让我…

前言

在数字化时代,地图服务已成为各类应用的标配,无论是导航、位置分享还是商业分析,地图都扮演着不可或缺的角色。然而,高质量的地图服务往往伴随着不菲的授权费用。公司原先使用的是国内某知名地图服务,但随着业务的扩展和成本的考量,老板决定寻找一种成本更低的解决方案。于是,我们的目光转向了免费的地图服务——天地图。

天地图简介

天地图(lbs.tianditu.gov.cn/server/guid…
是中国领先的在线地图服务之一,提供全面的地理信息服务。它的API支持地理编码、逆地理编码、周边搜索等多种功能,且完全免费。这正是我们需要的。

具体实现代码

为了将天地图集成到我们的系统中,我们需要进行一系列的开发工作。以下是实现过程中的关键代码段。

1. 逆地理编码

逆地理编码是将经纬度转换为可读的地址。在天地图中,这一功能可以通过以下代码实现:

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);
    Response 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;
}

2. 周边搜索

周边搜索允许我们根据一个地点的经纬度搜索附近的其他地点。实现代码如下:

public static List 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);
    Response response = HttpClientHelper.getWithoutUserAgent(request, null);
    List 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;
}

3. 文本搜索

文本搜索功能允许用户根据关键词搜索地点。实现代码如下:

public static List 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);
    Response response = HttpClientHelper.getWithoutUserAgent(request, null);
    List 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;
}

4. 坐标系转换

由于天地图使用的是WGS84坐标系,而国内常用的是GCJ-02坐标系,因此我们需要进行坐标转换。以下是坐标转换的工具类:



public class GCJ02_WGS84Utils {

    public static double pi = 3.1415926535897932384626;
    public static double a = 6378245.0;
    public static double ee = 0.00669342162296594323;

    
    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;
    }

    
    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可能存在访问延迟或不稳定的情况,建议在生产环境中做好异常处理和备用方案。
  • 坐标系转换是一个复杂的过程,确保使用可靠的算法和工具进行转换,以保证定位的准确性。

通过这次集成,我们不仅为公司节省了成本,还提升了系统的稳定性和用户体验。在未来的开发中,我们将继续探索更多高效、低成本的技术解决方案。

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22270,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?