0
点赞
收藏
分享

微信扫一扫

谷歌街景下载(二)

_刘彦辉 2022-04-27 阅读 52
算法java

上一篇文章讲述其原理。该文章用java实现:
谷歌街景下载(一)

创建一个正常springBoot,数据库用SQLite。
其中每一个SQLite,可以根据自己规律来划分,我这里的规律为经纬度来进行划分大小。例如:
我要下载一个经纬度大小的一块区域:
{“minLon”:120.179443359375,“maxLat”:23.7744140625,“minLat”:23.763427734375,“maxLon”:120.1904296875,“sqlname”:“11231”}
根据经纬度划分的一个矩形区域,其下载方式分两种:
方案一:暴力破解,给定的区域可以用双循环的方式,每个点平移经纬度小数点后六位的方式进行暴力破解,谷歌地图相邻点大概6米间距。暴力破解耗时高,并且会有遗漏。方案二不做赘述。
方案二:拿到了该矩形的区域后,首先对改区域的边界进行暴力破解:

 List<String> stringListAll = new ArrayList<>();
        //4条边分别使用
        stringListAll.addAll(linePoint(north, south, east, 0));
        stringListAll.addAll(linePoint(north, south, west, 0));
        stringListAll.addAll(linePoint(east, west, north, 1));
        stringListAll.addAll(linePoint(east, west, south, 1));
        List<String> stringList1 = stringListAll.stream().distinct().collect(Collectors.toList());

4条边调用4次,用一个list来接收后,需要进行去重。

public List<String> linePoint(BigDecimal north, BigDecimal south, BigDecimal sitePoint, int lineFlag) throws IOException {
        //经度,表示每一个点位平移的距离
        //BigDecimal up = new BigDecimal("0.0001");
        BigDecimal up = new BigDecimal("0.00005");
        List<String> panoidList = new ArrayList<>();
        for (BigDecimal i = south; i.compareTo(north) < 1; i = i.add(up)) {
            double weidu = i.setScale(8, ROUND_HALF_UP).doubleValue();
            double jingdu = sitePoint.setScale(8, ROUND_HALF_UP).doubleValue();
            String street;
            //http://cbk0.google.com/cbk?output=json&oe=utf-8&dm=1&pm=1&ll=纬度%2C经度&hl=zh-CN
            if (lineFlag == 0) {
                street = "http://cbk0.google.com/cbk?output=json&oe=utf-8&dm=1&pm=1&ll=" + weidu + "%2C" + jingdu + "&hl=zh-CN";
            } else {
                street = "http://cbk0.google.com/cbk?output=json&oe=utf-8&dm=1&pm=1&ll=" + jingdu + "%2C" + weidu + "&hl=zh-CN";
            }
            log.info(street);
            byte[] urlData = ProtoBfUtils.getUrl3D(street);
            String res = new String(urlData);
            if (!res.equals("{}")) {
                JSONObject json;
                json = JSONObject.parseObject(res);
                String panoId = json.getJSONObject("Location").getString("panoId");
                panoidList.add(panoId);
            }
        }
        return panoidList;
    }

public static byte[] getUrl3D(String fileUrl) throws IOException {
        HttpGet httpGet = new HttpGet(fileUrl);
        httpGet.addHeader("User-Agent", USER_AGENT);
        httpGet.addHeader("Referer", "https://earth.google.com/");
        CloseableHttpClient httpClient = HttpClients.createDefault();
        @Cleanup CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        log.info("url:" + httpGet.getURI().toString() + ",响应码:" + statusCode);
        if (statusCode == HttpStatus.SC_OK) {
            HttpEntity entity = httpResponse.getEntity();
            byte[] bytes = EntityUtils.toByteArray(entity);
            httpClient.close();
            return bytes;
        }else if (statusCode == HttpStatus.SC_BAD_REQUEST) {
            return null;
        }else if (statusCode == HttpStatus.SC_NOT_FOUND) {
            return null;
        }else if (statusCode == HttpStatus.SC_FORBIDDEN) {
            return null;
        }else if (statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
            return null;
        }else {
            EntityUtils.consume(httpResponse.getEntity());
            httpClient.close();
            //throw new CustomException(CustomException.STOP_TASK);
            throw new CustomException(CustomException.GOOGLE_SERVER_EXCEPTION + statusCode + "。url:" + httpGet.getURI().toString());
        }
    }

当前方法内只去获取json里面的panoid这个唯一标识。
通过以上方法能拿到该区域的所有边界上的点位,点位与点位之间在json中可以使用"links"这个标识来识别所有的相邻点,那么该区域就可以使用递归的方法获取到整个有效的点位。
接下来介绍递归获取内部点位:
首先两个实体类:

@Data
public class StreetView {
    private String panoid;
    private double lat;
    private double lng;
    private String json;
    }

public class Bbox {
    //最小经度,West
    private double West;
    //最小纬度 South
    private double South;
    //最大经度 East
    private double East;
    //最大纬度North
    private double North;
    }

开始递归:

    public Set<StreetView> downloadStreet2(List<String> stringListAll, Bbox bbox) throws IOException {
        //获取相邻点位
        //List<String> panoidListAll = new ArrayList<>();
        Set<StreetView> streetViewSet = new HashSet<>();
        Map<String,StreetView> streetViewMap=new HashMap<>();
        //创建一个对象 用来存需要的点位包含经纬度,StreetView
        for (int i = 0; i < stringListAll.size(); i++) {
            Map<String, StreetView> streetViewMap1 = checkPoint(stringListAll.get(i), bbox, streetViewMap);
            for (Map.Entry<String, StreetView> entry:streetViewMap1.entrySet()
                 ) {
                streetViewSet.add(entry.getValue());
                //log.info("当前存入点位的panoid:"+entry.getKey());
            }
        }

        return streetViewSet;
    }



 public Map<String,StreetView> checkPoint(String panoid, Bbox bbox, Map<String,StreetView> stringStreetViewMap) throws IOException {
        String url = "http://cbk0.google.com/cbk?output=json&oe=utf-8&dm=1&pm=1&panoid=" + panoid + "&hl=zh-CN";
        byte[] urlData = ProtoBfUtils.getUrl3D(url);
        String res = new String(urlData);
        if (!res.equals("{}")) {
            JSONObject json;
            json = JSONObject.parseObject(res);
            JSONArray links = json.getJSONArray("Links");

            if( !CollectionUtils.isEmpty(links)){
                for (int i = 0; i < links.size(); i++) {
                    JSONObject panoidJson = (JSONObject) JSONObject.toJSON(links.get(i));
                    String panoIdLink = (String) panoidJson.get("panoId");
                    String urllink = "http://cbk0.google.com/cbk?output=json&oe=utf-8&dm=1&pm=1&panoid=" + panoIdLink + "&hl=zh-CN";
                    byte[] urlDataLink = ProtoBfUtils.getUrl3D(urllink);
                    String resLink = new String(urlDataLink);
                    JSONObject jsonLink = JSONObject.parseObject(resLink);
                    BigDecimal original_lng = new BigDecimal(jsonLink.getJSONObject("Location").getString("lng"));
                    //纬度
                    BigDecimal original_lat = new BigDecimal(jsonLink.getJSONObject("Location").getString("lat"));
                    if (checkArea(original_lng, original_lat, bbox)) {
                        StreetView streetView = new StreetView(panoIdLink, original_lat.doubleValue(), original_lng.doubleValue(), resLink);
                        //map<panoid,streetView>
                        if(stringStreetViewMap.get(panoIdLink)==null){
                            // log.info("当前:"+panoIdLink);
                            stringStreetViewMap.put(panoIdLink,streetView);
                            checkPoint(panoIdLink, bbox, stringStreetViewMap);
                        }

                    }
                }
            }

        }
        return stringStreetViewMap;
    }
//该方法判断该点位是否在矩形范围内
    public boolean checkArea(BigDecimal original_lng, BigDecimal original_lat, Bbox bbox) {
        BigDecimal north = new BigDecimal(bbox.getNorth());
        BigDecimal south = new BigDecimal(bbox.getSouth());
        BigDecimal east = new BigDecimal(bbox.getEast());
        BigDecimal west = new BigDecimal(bbox.getWest());
        //坐标点与bbox范围对比
        if ((original_lat.compareTo(north) == -1) && (original_lat.compareTo(south) == 1) &&
                (original_lng.compareTo(east) == -1) && (original_lng.compareTo(west) == 1)) {
            return true;
        }
        return false;
    }

最后的这个list

Set<StreetView> streetViewSet = downloadStreet2(stringList1, bbox);

是装下了改区域的所有点位。
点位的数量多少与区域的大小和街道的密集程度相关。这里进行存入数据库有存在以下问题:点位数量太多会导致内存溢出,解决方式可以把给定的区域进行分割后,在进行下载。
把一块区域分4快的方法:

public  List<Bbox> spirtBbox(Bbox bbox){
        BigDecimal east =new BigDecimal(bbox.getEast());
        BigDecimal west =new BigDecimal(bbox.getWest());
        BigDecimal north =new BigDecimal(bbox.getNorth());
        BigDecimal south =new BigDecimal(bbox.getSouth());
        //大区域分割为4块区域。
        BigDecimal add = east.add(west);
        BigDecimal add2 = north.add(south);
        BigDecimal b2 = new BigDecimal("2");
        BigDecimal divide = add.divide(b2, 6, BigDecimal.ROUND_DOWN);
        BigDecimal divide2 = add2.divide(b2, 6, BigDecimal.ROUND_DOWN);
        double st = divide.doubleValue();
        double orh = divide2.doubleValue();
        //4位数重新封装:
        // Bbox bbox = new Bbox(latLonBox.getWest(), latLonBox.getSouth(), latLonBox.getEast(), latLonBox.getNorth());
        Bbox bbox1=new Bbox(bbox.getWest(),orh,st,bbox.getNorth());

        Bbox bbox2=new Bbox(st,orh,bbox.getEast(),bbox.getNorth());

        Bbox bbox3=new Bbox(bbox.getWest(),bbox.getSouth(),st,orh);

        Bbox bbox4=new Bbox(st,bbox.getSouth(),bbox.getEast(),orh);

        List<Bbox> bboxes=new ArrayList<>();
        bboxes.add(bbox1);
        bboxes.add(bbox2);
        bboxes.add(bbox3);
        bboxes.add(bbox4);
        return bboxes;
    }

区域点位街道路网给复杂的情况下:checkPoint这个方法递归层数太多也会报错,所有最好的方式就是划小区域。
在这里插入图片描述
付一张数据库存储样例:
在这里插入图片描述
在这里插入图片描述
以上是所有点位的下载。
步骤二下载图片:

    public  void  streetViewChangeListForID(String panoid,SqlSessionFactory sqlSessionFactory) throws IOException {
        log.info("当前panoid为: "+panoid);
        List<PanoidPojo>  panoidPojoList=new ArrayList<>();
        for (int x = 0; x <=7 ; x++) {
            for (int y = 0; y <=3 ; y++) {
                String picUrlLe3=
                        "http://cbk0.google.com/cbk?output=tile&panoid="+panoid+"&zoom=3&x="+x+"&y="+y;
                byte[] urlDataLe6 = ProtoBfUtils.getUrl3D(picUrlLe3);
                if (urlDataLe6!=null){
                    PanoidPojo panoidPojo=new PanoidPojo(panoid,x,y,urlDataLe6);
                    panoidPojoList.add(panoidPojo);
                }
            }
        }
        //这里做批量新增
        DBUtils.insetTileDb(sqlSessionFactory,panoidPojoList);
    }

通过上一张表拿到所有panoid 然后进行下载图片。
在这里插入图片描述
在此所有下载完毕

举报

相关推荐

0 条评论