百度瓦片地圖製作

小何117KNO發表於2017-08-24

在網上查詢了很久,找了很多百度瓦片地圖下載文章,但是並沒有我想要如何下載百度地圖操作的文章教程。通過同事指點加自我摸索理解,總結如下關於下載百度瓦片地圖製作的教程。

我們下載某個地區的地圖,首先我們需要獲取 圖塊座標

百度地圖API在展示地圖時是將整個地圖圖片切割成若干圖塊來顯示的,當地圖初始化或是地圖級別、中心點位置發生變化時,地圖API會根據當前畫素座標計算出視野內需要的圖塊座標(也叫圖塊編號),從而載入對應的圖塊用以顯示地圖。
百度地圖的圖塊座標原點與平面座標一致,從原點向右上方開始編號為0, 0:
這裡寫圖片描述
如何知道某個位置的圖塊座標呢?通過如下公式計算即可(這裡為向下取整):
圖塊座標 =|畫素座標 ÷ 256|
256實際上是每個圖塊的寬度和高度,我們用畫素座標除以這個數就知道圖塊座標了。還以天安門為例,在第4級下天安門所在的圖塊編號為:3, 1,而在第18級下,圖塊編號為:50617, 18851

如何獲取百度圖塊座標?
開啟百度開發平臺:http://lbsyun.baidu.com/jsdemo.htm?a#i7_1
將如下程式碼放入編輯器執行:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>地圖座標概念</title>
<script src="http://api.map.baidu.com/api?v=1.2"></script>
</head>
<body>
<div id="map_container" style="width:500px;height:320px;"></div>
<script>
var map =new BMap.Map('map_container', {defaultCursor: 'default'});
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);

var TILE_SIZE =256;

map.addEventListener('click', function(e){
var info =new BMap.InfoWindow('', {width: 260});
var projection =this.getMapType().getProjection();

var lngLat = e.point; 
var lngLatStr ="經緯度:"+ lngLat.lng +", "+ lngLat.lat;

var worldCoordinate = projection.lngLatToPoint(lngLat);
var worldCoordStr ="<br />平面座標:"+ worldCoordinate.x +", "+ worldCoordinate.y;

var pixelCoordinate =new BMap.Pixel(Math.floor(worldCoordinate.x * Math.pow(2, this.getZoom() -18)), 
Math.floor(worldCoordinate.y * Math.pow(2, this.getZoom() -18)));
var pixelCoordStr ="<br />畫素座標:"+ pixelCoordinate.x +", "+ pixelCoordinate.y;

var tileCoordinate =new BMap.Pixel(Math.floor(pixelCoordinate.x /256),
Math.floor(pixelCoordinate.y /256));
var tileCoordStr ="<br />圖塊座標:"+ tileCoordinate.x +", "+ tileCoordinate.y;

var viewportCoordinate = map.pointToPixel(lngLat);
var viewportCoordStr ="<br />可視區域座標:"+ viewportCoordinate.x +", "+ viewportCoordinate.y;

var overlayCoordinate = map.pointToOverlayPixel(lngLat);
var overlayCoordStr ="<br />覆蓋物座標:"+ overlayCoordinate.x +", "+ overlayCoordinate.y;

info.setContent(lngLatStr + worldCoordStr + pixelCoordStr + tileCoordStr + 
viewportCoordStr + overlayCoordStr);
map.openInfoWindow(info, lngLat);
});
</script>
</body>
</html>

執行出來之後點選地圖上會出現如圖:
這裡寫圖片描述
如此可以輕鬆地獲取圖塊座標資訊。

如何下載百度瓦片?
通過上述可以輕鬆獲取到地域圖塊座標範圍值。
提供如下Java程式碼下載瓦片圖片:

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * <pre>
 * 參考資料:
 * <a href="http://www.jianshu.com/p/0b292688b6af">國內主要地圖瓦片座標系定義及計算原理</a>
 * <a href="http://www.cnblogs.com/xiaozhi_5638/p/4748186.html">百度地圖根據經緯度計算瓦片行列號</a>
 *
 */
public class R implements Runnable {

    static int succeed = 0;
    static int error = 0;
    static int finishedThread = 0;
    static long endTime = 0;
    static String sPath = "http://online1.map.bdimg.com/onlinelabel/?qt=tile&x=%d&y=%d&z=%d&styles=pl&udt=20150924&scaler=1";
    /**
    * z : 層級
    * xstart : x的開始值
    * xend  : x的最大值
    * ystart  : y的開始值
    * yend  : y的最大值
    * threads : 幾個執行緒開始下載地圖 
    */
    int z=11, xstart = 407,xend = 409,ystart = 113,yend = 116,threads = 1;
    //下載瓦片地圖存放地址
    String dir = ""; 
    //是否覆蓋之前下載的圖片
    boolean overwrite = false; 

    Map<String, String> map = Maps.newHashMap();
    public R(Map<String, String> map) {
        this.map = map;
    }

    public static void main(String[] args) {

        final Map<String, String> map = Maps.newHashMap();

        /*Lists.newArrayList(args).forEach(s -> {
            String k = s.split("=")[0];
            String v = s.split("=")[1];
            map.put(k, v);
        });*/
        List<String> list = Lists.newArrayList(args);
        for(String str : list){
            String k = str.split("=")[0];
            String v = str.split("=")[1];
            map.put(k, v);
        }
        new R(map).run();

    }

    public void run() {
        ExecutorService exec = Executors.newCachedThreadPool();

        Set<Point> set = Sets.newHashSet();

        int total = (xend - xstart) * (yend - ystart);

        int split = total / threads;
        int count = 0;
        for(int i=xstart;i<=xend;i++) {
            for(int j=ystart;j<yend;j++) {
                set.add(new Point(i, j));
                if(++count % split == 0) {
                    exec.execute(new DownloadThread(set));
                    set = Sets.newHashSet();
                }
            }
        }
        if(set.size()>0) {
            exec.execute(new DownloadThread(set));
        }

        long t = System.currentTimeMillis();
        while (finishedThread < threads) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
            System.out.println("耗時 " + (System.currentTimeMillis() - t) / 1000 + "s 成功 " + succeed + " 失敗 " + error + " ok="+finishedThread+" total = "+total);
        }
        exec.shutdown();
        System.out.println("完成 共耗時 " + (endTime - t) / 1000 + "s 成功 " + succeed + " 失敗 " + error + " ok="+finishedThread+" total = "+total);
    }

    class DownloadThread extends Thread {
        Set<Point> set;
        public DownloadThread(Set<Point> set) {
            this.set = set;
        }

        public void run() {
            System.out.println(getName() + " started, task size = " + set.size());
            File base = new File(dir, z + "");

            for(Point point : set) {
                int x = point.x;
                int y = point.y;
                File path = new File(base, x + "");
                if (!path.exists()) {
                    path.mkdirs();
                }
                File file = new File(path, y + ".jpg");
                if (!file.exists() || overwrite) {
                    String url = String.format(sPath, x, y, z);
                    boolean b = save(url, file);
                    if (b) {
                        succeed++;
                        System.out.println(file.getAbsolutePath());
                    } else {
                        error++;
                    }
                }
            }
            System.out.println(getName()+" finished");
            finishedThread++;
            endTime = System.currentTimeMillis();
        }

        byte[] b = new byte[4096];
        int s = 0;

        boolean save(String path, File file) {
            InputStream is = null;
            OutputStream os = null;
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.connect();
                is = conn.getInputStream();
                os = new FileOutputStream(file);
                while ((s = is.read(b)) != -1) {
                    os.write(b, 0, s);
                }
                return true;
            } catch (Exception e) {
                return false;
            } finally {
                if (os != null) {
                    try {
                        os.flush();
                        os.close();
                    } catch (Exception e) {
                    }
                }
                if (is != null) {
                    try {
                        is.close();
                    } catch (Exception e) {
                    }
                }
            }
        }
    }

    static class Point {
        int x, y;
        Point(int x,int y){
            this.x = x;
            this.y = y;
        }
    }
}

通過上述java程式碼,修改其中引數值,百度瓦片地圖下載到指定路徑下。

相關文章