精华内容
下载资源
问答
  • mapbox矢量切片
    2020-09-25 12:39:54

    python3下载mapbox矢量切片

    通过观察mapbox的页面开发者工具里的network可以发现,打开矢量切片和字体切片pbf和prite图标的链接,即可下载文件。所以写了个python程序不断请求mapbox的页面,下载矢量切片。用同样的方法可以下载mapbox的style的json文件和字体文件。

    import requests
    import os
    
    
    def download(z, x, y, n):
        url1 = 'https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7/'
        url3 = '?sku=101QpzaqVFURD&access_token=your token'
        url2 = str(z)+'/'+str(x)+'/'+str(y)+'.vector.pbf'
        url = url1+url2+url3
    
        req = requests.get(url)
        filename = str(n-y)+'.vector.pbf'
        print(url.split('/')[-3]+'/'+url.split('/')[-2]+'/'+filename)
        if req.status_code != 200:
            print('下载异常')
            return False
        try:
            # 创建文件夹
            path = "d:/wojtekP/map/"+url.split('/')[-3]+'/'+url.split('/')[-2]
            isExist = os.path.exists(path)
            if not isExist:
                os.makedirs(path)
            with open(path+'/'+filename, 'wb+') as f:
                # req.content为获取html的内容
                f.write(req.content)
                print('下载成功')
        except Exception as e:
            print(e)
    
    
    if __name__ == '__main__':
        # y坐标逆序命名
        # x与y的值每层按指数递增,第z层,x,y取值范围都为0----2^z-1,center[-180,-90]为x,y最大处
    
        # # 第0到第5层的每个pbf矢量切片全下
        for z in range(0, 6):
            for x in range(0, 2**z):
                for y in range(0, 2**z):
                    download(z, x, y, 2**z-1)
    
        # # 第6层到第10层开始,下一部分(广东)的矢量切片
        z = 6
        n = 2**z-1
        for x in range(50, 55):
            for y in range(25, 30):
                download(z, x, y, n)
    
        z = 7
        n = 2**z-1
        for x in range(100, 110):
            for y in range(50, 60):
                download(z, x, y, n)
        # 图层过多切片文件,可以多个python文件多线程分范围分别下载
        z = 8
        n = 2**z-1
        for x in range(200, 220):
            for y in range(100, 120):
                download(z, x, y, n)
    
        z = 9
        n = 2**z-1
        for x in range(400, 440):
            for y in range(200, 240):
                download(z, x, y, n)
    
        z = 10
        n = 2**z-1
        for x in range(800, 880):
            for y in range(400, 480):
                download(z, x, y, n)
        # 第11层开始,下一部分(广州)的矢量切片
        z = 11
        n = 2**z-1
        for x in range(1640, 1680):
            for y in range(860, 900):
                download(z, x, y, n)
    
        z = 12
        n = 2**z-1
        for x in range(3320, 3350):
            for y in range(1760, 1800):
                download(z, x, y, n)
    
        z = 13
        n = 2**z-1
        for x in range(6650, 6690):
            for y in range(3540, 3570):
                download(z, x, y, n)
    
        z = 14
        n = 2**z-1
        for x in range(13320, 13370):
            for y in range(7090, 7130):
                download(z, x, y, n)
    
        z = 15
        n = 2**z-1
        for x in range(26640, 26720):
            for y in range(14180, 14240):
                download(z, x, y, n)
    
        z = 16
        n = 2**z-1
        for x in range(53300, 53440):
            for y in range(28350, 28510):
                download(z, x, y, n)
    
    
    更多相关内容
  • 使用GDAL生成Mapbox Vector Tiles矢量切片,个人觉得比tippecanoe更加强大。已使用leaflet测试加载成功。其实tippecanoe本质上也是调用ogr2ogr。使用GDAL可完全取代tippecanoe,同时可以克服tippecanoe 只能支持...
  • //高德地图(6:影像,7:矢量,8:影像路网) DecimalFormat df=new DecimalFormat("0.00"); System.out.println("X:("+minC+"->"+maxC+"); Y:("+minR+"->"+maxR+")。"); System.out.println("第"+z+"级总共将会有:...

    package com.aerors.tiles;

    import java.io.DataInputStream;

    import java.io.DataOutputStream;

    import java.io.File;

    import java.io.FileOutputStream;

    import java.io.IOException;

    import java.net.HttpURLConnection;

    import java.net.SocketTimeoutException;

    import java.net.URL;

    import java.text.DecimalFormat;

    import java.util.ArrayList;

    import java.util.HashMap;

    import java.util.Iterator;

    import java.util.Map;

    public class tianditiles {

    private static Map tempMap = new HashMap();

    /**

    * 远程文件下载

    *

    * @param url 下载地址

    * @param file 保存文件地址

    */

    public static boolean download(String zxystr,URL url, File file) throws IOException {

    boolean flag = true;

    DataOutputStream dos = null;

    DataInputStream dis = null;

    try {

    if (!file.getParentFile().exists()) file.getParentFile().mkdirs();

    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

    conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

    conn.setConnectTimeout(6000);

    conn.setReadTimeout(20000);

    conn.connect();

    dos = new DataOutputStream(new FileOutputStream(file));

    dis = new DataInputStream(conn.getInputStream());

    byte[] data = new byte[2048];

    int i = 0;

    while ((i = dis.read(data)) != -1) {

    dos.write(data, 0, i);

    }

    dos.flush();

    }catch (SocketTimeoutException e){

    tempMap.put(e,zxystr);

    System.out.println(zxystr);

    System.out.println(e);

    } catch (IOException e) {

    flag = false;

    System.out.println(e);

    } catch (Exception e) {

    flag = false;

    System.out.println(e);

    }

    finally {

    if (dis != null) dis.close();

    if (dos != null) dos.close();

    }

    return flag;

    }

    /**

    * 计算分辨率

    *

    * @param maxLevel 最大级别

    */

    public static double[] getResolutions(int maxLevel) {

    double max = 360.0 / 256.0;

    double[] resolutions = new double[maxLevel + 1];

    for (int z = 0; z <= maxLevel; z++) resolutions[z] = max / Math.pow(2, z);

    return resolutions;

    }

    /**

    * 根据经度获取切片规范下的行号

    *

    * @param lon

    * @param zoom

    * @return

    */

    public static int getOSMTileXFromLongitude(double lon, int zoom) {

    return (int) (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));

    }

    /**

    * 根据纬度获取切片规范下的列号

    *

    * @param lat

    * @param zoom

    * @return

    */

    public static int getOSMTileYFromLatitude(double lat, int zoom) {

    return (int) (Math.floor((1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom)));

    }

    public static void main(String[] arg) throws IOException {

    // String type = "ArcGIS";

    String type = "CUSTOM";

    // double[] extent = {68, 11, 135, 54};//中国

    // double[] extent = {113, 23, 129, 35.26};//华东(广州-釜山)

    double[] extent = {117, 26, 123, 32};//浙江

    // for (int z = 10; z < 11; z++) {

    // double[] extent = {-180, -90, 180, 90};

    for (int z = 14; z <= 14; z++) {

    //计算行列号(使用瓦片的中心点经纬度计算)

    //起始结束行

    int minR = getOSMTileYFromLatitude(extent[3], z);

    int maxR = getOSMTileYFromLatitude(extent[1], z);

    //起始结束列

    int minC = getOSMTileXFromLongitude(extent[0], z);

    int maxC = getOSMTileXFromLongitude(extent[2], z);

    // for (int y = minR; y <= maxR; y++) {

    for (int y = 6937; y <= maxR; y++) {

    for (int x = minC; x <= maxC; x++) {

    String urlstr = "https://maps.tilehosting.com/data/v3/" + z + "/" + x + "/" + y +".pbf?key=yzgDrAunRjJjwhG6D3u7";

    // String urlstr = "https://b.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7/" + z + "/" + x + "/" + y +".vector.pbf?access_token=pk.eyJ1IjoibHhxanNzIiwiYSI6ImNqY3NkanRjajB1MWwzM3MwcnA0dDJrYngifQ.ApTVfmm2zBM_kF22DvtowQ";

    // String urlstr = "http://demo.zjditu.cn/vtiles/tdt_zj/" + z + "/" + x + "/" + (y-8054) +".mvt?v=20180831"; //天地图服务器t0-t8间选一个

    // String urlstr = "http://t0.tianditu.com/DataServer?T=vec_w&x="+x+"&y="+y+"&l="+z; //天地图服务器t0-t8间选一个

    // String urlstr = "http://mt2.google.cn/vt/lyrs=m&scale=1&hl=zh-CN&gl=cn&x="+x+"&y="+y+"&z="+z; //谷歌地图服务器t0-t2间选一个

    // String urlstr = "http://online3.map.bdimg.com/onlinelabel/?qt=tile&x="+x+"&y="+y+"&z="+z+"&&styles=pl&udt=20170712&scaler=1&p=1"; //百度地图(加密过的)

    // String urlstr = "http://c.tile.opencyclemap.org/cycle/" + z + "/" + x + "/" + y + ".png"; //osm地图

    // String urlstr = "http://wprd04.is.autonavi.com/appmaptile?x="+x+"&y="+y+"&z="+z+"&lang=zh_cn&size=1&scl=1&style=8"; //高德地图(6:影像,7:矢量,8:影像路网)

    DecimalFormat df=new DecimalFormat("0.00");

    System.out.println("X:("+minC+"->"+maxC+"); Y:("+minR+"->"+maxR+")。");

    System.out.println("第"+z+"级总共将会有:"+(maxC-minC+1)+"个文件夹,每个有"+(maxR-minR+1)+"个文件;总下载:"+(maxC-minC+1)*(maxR-minR+1)+"个文件;该级别下载进度:"+df.format(100*(y-minR)/(double)(maxR-minR+1))+"%");

    System.out.println(urlstr);

    String path = null;

    // if (type.equals("ArcGIS")) {

    // //ArcGIS格式瓦片下载

    // path = getTDTilesForArcGISPath(x, y, z);

    // } else {

    // //一般格式瓦片下载

    // path = getTDTilesForCustomPath(x, y, z);

    // }

    // path = getTDTilesForCustomPathMvt(x,y,z);

    path = getOpenstreetForCustomPathMvt(x,y,z);

    File file = new File(path);

    URL url = new URL(urlstr);

    String zxyStr = z+"/"+x+"/"+y;

    download(zxyStr,url, file);

    }

    }

    }

    Iterator iterator = tempMap.keySet().iterator();

    while (iterator.hasNext()) {

    Object key = iterator.next();

    System.out.println("tmp.get(key) is :"+tempMap.get(key));

    }

    }

    public static String getTDTilesForArcGISPath(int x, int y, int z) {

    String l = "L" + String.format("%02d", z);

    String r = "R" + makePath(y);

    String c = "C" + makePath(x);

    String path = "tile/" + l + File.separator + r + File.separator + c + ".png";

    return path;

    }

    public static String getTDTilesForCustomPath(int x, int y, int z) {

    String path = "tile/" + z + File.separator + y + File.separator + x + ".png";

    return path;

    }

    public static String getTDTilesForCustomPathMvt(int x, int y, int z) {

    String path = "tileMvt/" + z + File.separator + y + File.separator + x + ".mvt";

    return path;

    }

    public static String getOpenstreetForCustomPathMvt(int x, int y, int z) {

    String path = "tileOpenStreet/" + z + File.separator + x + File.separator + y + ".pbf";

    return path;

    }

    private static String makePath(int num) {

    String str = Integer.toHexString(num);

    //ArcGIS行列都是8位长度

    while (str.length() < 8) {

    str = "0" + str;

    }

    return str;

    }

    }

    展开全文
  • 博主由于在项目中有建立矢量瓦片地图服务的...所以利用Mapbox studio进行上传矢量数据并发布这一方案并不适合本项目,另一方面Geoserver2.11版本开始提供矢量瓦片插件,这使Geoserver能够支持矢量瓦片的生成与发布。 ...

            博主由于在项目中有建立矢量瓦片地图服务的需求,对矢量瓦片进行了相关研究,在此和大家分享我的解决方案。由于项目中矢量数据是需要保密的,因此需要研究出一套开源的本地化(离线)生成与发布的方案。所以利用Mapbox studio进行上传矢量数据并发布这一方案并不适合本项目,另一方面Geoserver2.11版本开始提供矢量瓦片插件,这使Geoserver能够支持矢量瓦片的生成与发布。

           Geoserver生成与发布矢量瓦片的方案操作简单,用户界面友好,本项目初期就利用了这一方案。关于Geoserver矢量瓦片的方案可以直接搜索相关技术博客,已经提供了详细的描述,这里我不再赘述。

           在实际项目当中,博主发现了Geoserver矢量切片方案存在一些较为致命的缺陷。首先,其矢量切片需要在发送地图请求后实时生成矢量瓦片(.pbf),虽然geoserver利用geowebcache进行矢量瓦片的缓存,但其效率依很长时间,然相对较低;若想直接完成各缩放级别的全部矢量瓦片的预生成,需要耗费很长时间,而且由于新版本中geowebcache都内嵌在geoserver当中,博主的项目出现过在矢量瓦片预生成过程中geoserver服务器down掉的情况(geowebcache与geoserver分布式部署的解决方案见博主的另一篇文章https://blog.csdn.net/luochanxiong6435/article/details/79731974)。另外, geoserver对矢量瓦片的管理依然采取类似WMTS栅格瓦片类似的文件系统管理方式,这种方案由于瓦片文件的碎片化而导致管理不够集约,效率较低。最后,由于博主的项目仅仅需要一个矢量瓦片的生成与发布方案,Geoserver相对量级较大,很多功能闲置,也造成服务器端的压力变大。

           考虑到上述问题,博主需要挖掘出另一套轻量的矢量瓦片本地化(离线)生成与发布方案。在经过反复测试与实验,博主选择了一条Tippecanoe + Nodejs的self-hosting矢量瓦片生成与发布方案。其中Tippecanoe是矢量瓦片生成工具,而Nodejs则是将矢量瓦片文件发布为Web服务的后台程序。下面对本方案进行一较为详细的介绍。

     

    1.矢量切片综述

           矢量瓦片类似于栅格瓦片,以多层次模型将矢量数据分割成为矢量要素描述性文件存储于服务器端, 在客户端根据指定的样式进行渲染绘图。矢量瓦片与栅格瓦片的不同之处是单个矢量瓦片存储投影在一个四方形区域的几何信息和属性信息,而不是预先渲染的栅格图片。矢量瓦片地图的实现首先需要将矢量数据的几何信息和属性信息分割为一组矢量瓦片存储在服务器端,客户端根据地图显示范围和样式文件定义的数据源通过分布式网络获取矢量瓦片、地图标注字体、图标等数据源, 然后根据样式文件定义的地图表达规则在客户端即时渲染输出地图。

           目前矢量瓦片尚无统一数据标准,Mapbox基于Google protocol buffers制定的开源矢量瓦片数据标准 MAPBOX VECTOR TILE SPECIFICATION,是目前较为通用的矢量瓦片数据标准,已被多个公司和组织采用。早期Mapnik也制定了矢量瓦片数据标准(mapnik-vector-tile)。目前矢量瓦片的格式主要有三种:mvt(pbf)、geojson、topojson,其中压缩率和传输速度最好的是mvt格式,这里也着重介绍这一矢量瓦片格式。

           矢量瓦片技术主要包括三个研究方面:矢量瓦片数据组织、矢量瓦片存储管理和矢量瓦片渲染引擎。其具体细节如下图1所示。

    图1.矢量瓦片技术研究综述

    1.1   矢量瓦片数据组织

     

    1.1.1 瓦片数据集组织模型

            矢量瓦片数据组织可分为两个层次:①瓦片数据集组织模型;②单个瓦片内要素的组织模型。

            矢量瓦片数据集的组织模型类似栅格瓦片金字塔模型,可通过自定义矢量瓦片的大地坐标系、投影方式和瓦片编号方案实现任意精度、任意空间位置与矢量瓦片的对应关系。为了与目前的栅格瓦片相关服务规范(如OGC WMTS等)相兼容以及便于将矢量瓦片转换为栅格瓦片,矢量瓦片一般采用与栅格瓦片相同的投影方式和瓦片编号方式。Mapbox矢量瓦片默认的大地坐标系为 WGS-84(EPSG:4326),投影方式为球面墨卡托(Web Mercator(EPSG:3857)),瓦片编号采用Google瓦片方案。因此,Mapbox矢量瓦片的大地坐标系、投影坐标系、像素坐标系和瓦片坐标系与栅格瓦片一致。

            为了便于矢量瓦片数据集的网络传输和数据库存储,推荐将矢量瓦片数据集打包生成矢量瓦片包,常用的有ArcGIS矢量瓦片包(VTPK格式)和可存储到 SQLite数据库的MBTiles格式等。其中MBTiles是Mapbox推荐的矢量瓦片数据集格式,其本质上是以数据库的形式对矢量瓦片进行集中式管理,包含有metadata、sqlite_stat1、tiles三个表,name、tile_index两个索引。使用sqlite3可以打开mbtiles数据库直接进行查看,如下图2所示。

     

    图2.mbtiles数据库内容组织

    1.1.2 单瓦片组织模型

           单个矢量瓦片在逻辑上可以通过图层组织要素信息。每个图层所包含要素的几何信息和属性信息在逻辑上分开存储。以 Mapbox矢量瓦片为例,其逻辑存储 结构是Tile->Layer->geometry& metadata。其中几何要素分为点、线、面和未知要素类。未知要素类是 Mapbox 特意设置的一种要素类型,解码器可以尝试解码未知的要素类型,也可以选择忽略这种要素类型的要素。元数据信息又分为图层属性和要素属性。每块矢量瓦片至少包含一个图层,每个图层至少包含一个要素。

           矢量瓦片的物理模型是瓦片属性信息和位置信息在存储过程中的具体表现形式。描述矢量瓦片属性信息和几何位置信息的文件常有 GeoJson(.json); TopoJSON(.topojson)和 Google Protocol Buffers (PBF)。 其中GeoJSON是一种基于Javascript对象表示法的地理空间信息数据交换格式,易于阅读,通用性强,大多数软件可以直接打开,但存储地理数据较多时易产生冗余信息。TopoJSON是在GeoJSON基础上对共享边界几何要素拓扑编码,减少冗余信息的一种优化数据格式,被 Mapzen推荐作为矢量瓦片的存储格式。Google Protocol Buffers(PBF)是一种轻便高效的结构化数据存储格式,Mapbox矢量瓦片采用 PBF格式组织单个瓦片内要素的信息,相比GeoJSON格式的矢量瓦片文件体积更小,解析速度更快。

           单个mvt矢量瓦片的组织编码分为几何编码和属性编码两部分,对应矢量数据的空间信息和属性信息。PBF格式的矢量瓦片存储几何信息所用坐标系定义为以瓦片左上角为原点,X 方向向右为正, Y方向向下为正,坐标值以格网数为单位。单个矢量瓦片默认的格网数为4096×4096,即使4K(屏幕分辩率为4096 px×4096px)的高清屏上只显示一张矢量瓦片也不会出现类似于栅格瓦片的锯齿效果。屏幕分辨率越高,可以提供相应的提高瓦片格网数量,以精确记录瓦片内要素的几何位置信息。具体的编码方式如下图3所示。

     

    图3.Mapbox矢量瓦片编码方式

    1.1   矢量瓦片渲染引擎

            OpenGL、WebGL等客户端图形渲染技术的发展,推动了地图渲染引擎的快速发展。WebGL是一项可以在浏览器中流畅展示3D模型和场景的一种技术,它使 用javascript作为编程语言,调用浏览器支持的3D绘制函数,来实现3D模型和场景的展现。因为浏览器实现了OpenGLES的规范,这套规范可以直接使用指令操作显卡,使显卡渲染的3D世界,直接反映到浏览器中。

           OpenGL、WebGL是底层3D绘图技术标准,为了便于地图的渲染输出,Mapzen 采用Tangram作为地图渲染引擎。Tangram是一个开源的3D渲染引擎,专门用于绘制地图,使用OpenGL图形API。它能够解析各种来源的矢量数据,然后在客户端实时渲染生成带有几何图形、文字标注、图示符号的3D场景地图。Tangram 提供浏览器渲染引擎(Tangram-JS)和移动终端渲染引擎(Tangram-ES)。

            Mapbox采用Mapbox GL作为地图渲染引擎,类似于Tangram,提供了浏览器渲染引擎(Mapbox GL JS)和移动终端渲染引擎(Mapbox GL Native)。Mapbox样式文件是定义地图视觉效果的Json文件,它定义了绘制地图数据源、绘制顺序以及绘制样式等规则。根据样式文件的信息,利用 Mapbox GL JS可在浏览器中实时渲染出互动性强的3D场景地图。

    2.矢量切片算法与开源工具

            矢量切片的算法类似栅格瓦片,首先要进行瓦片的划分,生成行列号;然后根据行列号进行反解,算出各瓦片的地理坐标范围,按照地理坐标范围由17个点生成瓦片的范围矩形geometry,并使用这些瓦片范围geometry与源数据进行求交运算,划分出各矢量瓦片。这其中也涉及到不同缩放级别的矢量数据抽稀与简化,以保证各单个矢量瓦片的大小不超限和前端显示效果。

            而开源的矢量瓦片生成工具,除了geoserver这种通过插件式引入的方案,博主发现了以下三种轻量级的离线切片工具。其中,TileStache是基于Python开发的地图瓦片服务器端软件,相对稳定,从完成切片到切片服务发布集成化,但目前仅支持geojson格式的矢量切片,前端传输效率不佳;Tilelive是一套基于nodejs开发的地图瓦片生成工具,是Mapbox官方最早提供的一种切片工具,支持shp等多种数据源格式,可以生成mvt(pbf)格式的矢量切片,但安装较为困难,对系统版本以及配置限制严格,需要安装node-mapnik依赖,且切片效率较低。博主因为设备原因无法成功安装这一工具; Tippecanoe是基于C++开发的地图瓦片生成工具,是Mapbox官方目前推荐的切片工具,目前仅支持geojson格式的数据源(可以将shp通过ogr2ogr等工具快速转换为geojson),可以生成mbtiles格式pbf矢量切片数据包,安装简单,且切片效率极高,这也是博主所采用的切片工具。对矢量切片算法和开源工具的具体描述如下图4所示。

     

    图4.矢量切片算法与开源工具

     

    3.矢量瓦片本地化生成与发布方案

           实现矢量瓦片的self-hosting方案概略来说分为两个流程:矢量瓦片的生成与矢量瓦片的服务发布。这里博主采用采用的是Tippecanoe + NodeJS的矢量瓦片本地化生成与发布方案。其中Tippecanoe(https://github.com/mapbox/tippecanoe)作为Mapbox的开源项目,可以完成矢量瓦片的生成。使用NodeJS搭建轻量级的的服务端后台,通过express和mbtiles模块包可以轻易地将矢量瓦片数据集作为服务进行发布。本实验采用的解决方案流程如下图5所示。

     

    图5.矢量瓦片本地化生成发布方案

    3.1 矢量数据预处理

    由于Tippecanoe目前仅支持geojson格式的数据源输入,而项目中最常见的矢量数据格式则是shp,因此这里需要通过一些手段将shp数据转换为geojson数据。可以使用arcgis自带的工具进行转换也可以通过gdal的ogr2ogr工具完成转换。博主在实验中采用的是ogr2ogr工具。

    3.1.1 gdal的安装与配置

            这里博主采用的实验环境是ubuntu16.04系统, ogr2ogr工具在GDAL中,Linux安装GDAL需要分别安装PROJ.4、GEOS和GDAL,可以下载对应的包并通过make&make install进行安装,也可以直接使用命令:sudo apt-get install libproj-dev libgeos-dev libgdal-dev安装。安装完在终端输入gdalinfo,如果看到下图6所示的信息则表示安装成功。

     

    图6.gdal安装成功信息

        值得注意的是,如果在上述过程中出现提示共享库找不到,需要进行如下的配置处理:
    1、修改/etc/ld.so.conf文件,将共享库的路径“/usr/local/lib”加入进去。

    2、运行ldconfig 命令使其生效。注意凡是增加了新的共享库,都需要运行一下ldconfig 命令。

     

    3.1.2 数据格式转换

           在成功安装完gdal工具后,便可以使用ogr2ogr工具方便地完成postgis、shp与geojson等格式数据的相互转换了。

            本实验采用的源数据是shp格式,且数据个数较多,为了避免单个重复操作,因此可以编写一个shell进行批处理,具体代码如下所示:

    #!bin/sh
    for layer in "layer1" "layer2" "layer3"
    do
      echo "$layer convert start"
      ogr2ogr -f "GeoJSON" ./$layer.geojson "$layer.shp"
      echo "$layer convert successful"
    done
    

            这里我将shell保存为convert.sh,将其拷贝到原始数据shp的文件夹中,在命令行中执行:

    # cd /yourFilePath
    # source convert.sh

            这里值得注意的一点是,由于Mapbox目前仅支持EPSG:4326(WGS84)和EPSG:3857(Web墨卡托)两种投影,因此在进行转换前我们需要将shp数据的投影设置为上述两种,这里可以通过arcmap直接转换,或者通过ogr2ogr命令完成,假定我们要将shp数据转为EPSG:3857,可以输入如下操作:

    # ogr2ogr –t_srs EPSG:3857 <out_file.shp> <in_file.shp>

    3.2 矢量瓦片的生成

           首先,是安装Tippecanoe,这里可以直接从GitHub中下载其项目源码https://github.com/mapbox/tippecanoe/releases,然后解压并进入到对应文件夹,依次输入命令进行编译安装:

    # cd /TippecanoeFilePath
    # make
    # make install

            为了测试Tippecanoe是否安装成功,我们可以输入tippecanoe –v查看其版本,如果得到输出tippecanoev1.xx.xx则证明安装成功。

            接下来就是进行矢量瓦片的生成部分, Tippecanoe为用户提供了多种切片配置参数,如缩放级别、要素简化程度、筛选要素等,也可以选择输出mbtiles矢量瓦片数据集或pbf矢量瓦片文件两种格式,详细的参数说明可以参考说明https://github.com/mapbox/tippecanoe/blob/master/README.md

     

            本实验中使用的切片命令如下:

    # tippecanoe -z14 -pS -g3 -r1.25 -s EPSG:3857 –o yourData.mbtiles yourData.geojson

            其中各参数的配置参考上述链接,这里不予赘述。有一点值得注意的是,如果在上文数据源shp的投影设置为EPSG:4326,这里就可以不通过-s命令进行投影设置,因为默认采用的就是EPSG:4326;而如果上文采用EPSG:3857,这里就需要进行-s EPSG:3857进行投影设置。(注意:矢量切片配置参数的搭配要按照项目的实际需求进行不同的设置!)

           在完成上述命令后,可以在对应的文件夹中看到yourData.mbtiles矢量瓦片数据集,如果是linux系统,直接可以通过sqlite3进行查看。

     

    3.3 矢量瓦片服务发布

           在上一步骤中,我们通过tippecanoe工具完成了mbtiles格式的矢量瓦片数据集的生成,下面就需要通过一些方法将本地的矢量瓦片数据进行网络地图服务的发布。这里有很多不同的方式,其中比较简单的是通过PHP或Nodejs搭建简单的服务器程序,即可完成服务的发布。

           本实验采用Nodejs实现这一过程。本质上,矢量瓦片服务的发布就是将mbtiles数据库作为网络资源在服务器中对外公开,通过前端浏览器请求对应的缩放级别(z)、瓦片行号(x)、瓦片列号(y)参数,在后台对mbtiles进行sql查询并返回对应矢量瓦片数据的过程。

           实验中,需要引入express、mbtiles两个node包,编写TileService.js、server.js、config.js三对javascript模块。

           其中TileService.js对应代码如下所示:

    "use strict";
    var config = require('./config');
    var MBTiles = require('mbtiles');
    var path = require('path');
    var fs = require('fs');
    
    var TileService = function(query) {
    	this.q = query;
    };
    TileService.prototype.getInfo = function(done) {
      this._initMBTiles(function(err, mbtiles) {
        if (err) return done(err);
        mbtiles.getInfo(function(err, info) {
          if (err) return done(new Error('cannot get metadata'));
          done(null, info);
        });
      });
    };
    
    TileService.prototype.getTile = function(done) {
      var that = this;
      that._initMBTiles(function(err, mbtiles) {
        if (err) return done(err);
        mbtiles.getTile(that.q.params.z, that.q.params.x, that.q.params.y, function(err, tile, headers) {
          if (err) return done(err);
          done(null, tile, headers);
        });
      });
    };
    TileService.prototype._initMBTiles = function(done) {
      var mbtilesfile = path.join(config.TILES_DIR, this.q.params.ts + '.mbtiles');
      fs.exists(mbtilesfile, function (exists) {
        if (!exists) return done(new Error('cannot find MBTiles file on server: ' + mbtilesfile));
        new MBTiles(mbtilesfile, function(err, mbtiles) {
          if (err) return done(new Error('cannot initialize MBTiles object'));
          done(null, mbtiles);
        });
      });
    };
    
    module.exports = TileService;

            server.js代码如下所示:

    "use strict";
    var express = require('express');
    var app = exports.app = express();
    var config = require('./config');
    var TileService = require('./TileService');
    
    var routeHandlers = {
      getTile: function(req, res, next) {
        var tileService = new TileService(req);
        tileService.getTile(function(err, tile, headers) {
          if (err) return res.status(404).send(err.message);
          res.set(headers);
          res.send(tile);
        });
      },
    getInfo: function(req, res, next) {
        var tileService = new TileService(req);
        tileService.getInfo(function(err, info) {
          if (err) return res.status(404).send(err.message);
          res.json(info);
        });
      },
      ping: function(req, res, next){
        res.send('tilehut says pong!');
      },
      // openshift expects to get a valid response from '/' (health status)
      healthStatus: function(req, res, next){
        res.send(':)');
      },
    };
    app.disable('x-powered-by');
    app.use('*', function(req, res, next) {
      // set CORS response header
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'X-Requested-With');
      next();
    });
    
    app.use('/:ts/map', express.static(config.MAP_DIR));
    
    app.route('/:ts/:z/:x/:y.*grid.json$').get(routeHandlers.getGrid);
    app.route('/:ts/:z/:x/:y.*').get(routeHandlers.getTile);
    app.route('/:ts/meta.json').get(routeHandlers.getInfo);
    app.route('/ping').get(routeHandlers.ping);
    app.route('/').get(routeHandlers.healthStatus);
    
    app.listen(config.PORT, config.IPADDRESS, function() {
      console.info('TileServer on http://%s:%s', config.IPADDRESS, config.PORT);
    });

            config.js代码如下所示:

    "use strict";
    var path = require('path');
    var config = {
      PORT: process.env.OPENSHIFT_NODEJS_PORT || 8000,
      IPADDRESS: process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0',
    //mbtiles对应的文件位置
      TILES_DIR: process.env.OPENSHIFT_DATA_DIR || path.join(__dirname, '/data'),
      MAP_DIR: path.join(__dirname, '/static/map'),
    }
    module.exports = config;

            在工程文件夹中新建data文件夹,放入上述生成的mbtiles矢量瓦片数据集,然后在命令行中执行:

    # cd /yourNodeProjectPath
    # node server.js
    

            如果看见如下图所示的信息则证明服务启动成功。

     

        如果想要更简单的方式完成上述操作,可以参考下面的链接:https://gis.stackexchange.com/questions/125037/self-hosting-mapbox-vector-tiles#/.

     

    4.矢量瓦片服务前端调用

           针对发布好的矢量瓦片,这里采用Mapbox GL JS前端框架进行服务的调用与地图的展示。本实验的测试用的源数据为OSM下载的北京路网数据和美国行政区划数据,源数据如下图7所示:

     

      

    图7.测试原始shp数据

     

            编写对应的mapbox_vertor.html进行矢量瓦片地图的调用:

    <!DOCTYPE html>
    <html>
    <head>
       <meta charset='utf-8' />
       <title>Display a map</title>
       <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
       <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.41.0/mapbox-gl.js'></script>;
       <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.41.0/mapbox-gl.css' rel='stylesheet' />
       <style>
           body { margin:0; padding:0; }
           #map { position:absolute; top:0; bottom:0; width:100%; }
       </style>
    </head>
    <body>
    <div id='map'></div>
    <script>
       //使用注册Mapbox时候后台自动生成的token
       mapboxgl.accessToken = ‘yourToken’;
       var map = new mapboxgl.Map({
           container: 'map', // 放置的div的id
           style: 'mapbox://styles/mapbox/streets-v9',
           center: [116.466, 40.035],// 地图显示中心点位置
           zoom: 8,// 开始的缩放级别
           maxZoom: 16
       });
       map.on('load', function() {
           //访问本地的服务器
           var bjroad_tile_source = {
               type: 'vector',
               tiles: [
                   'http://localhost:8000/tiles-bjroad3857-vector/{z}/{x}/{y}.pbf'
               ],
               maxzoom: 14
           };
    	   var usa_tile_source = {
               type: 'vector',
               tiles: [
                   'http://localhost:8000/tile-usa4326-vector/{z}/{x}/{y}.pbf'
               ],
               maxzoom: 14
           };
    //注意:这里的'source-layer'要与mbtiles数据集中所使用的图层名称(id)保持一致!
           var bjroad_polygon_layer = {
               'id': 'bjroad_polygon_id',
               'type': 'fill',
               'source': 'bjroad_tile_source',
               'source-layer': 'BJ_LN_3857',  
               'paint': {
                   "fill-color": "#00ffff",
                   "fill-opacity": 1,
                   "fill-outline-color": "#ff0000"
               }
           };
           var bjroad_normal_line_layer = {
               'id': 'bjroad_normal_line_id',
               'type': 'line',
               'source': 'bjroad_tile_source',
               'source-layer': 'BJ_LN_3857',
               'layout': {
                   'line-join': 'round',
                   'line-cap': 'round'
               },
               'paint': {
                   "line-color": "#0000ff",
                   "line-width": 1
               }
           };
    	   var usa_polygon_layer = {
               'id': 'usa_polygon_id',
               'type': 'fill',
               'source': 'usa_tile_source',
               'source-layer': 'usa4326',  
               'paint': {
                   "fill-color": "#00ffff",
                   "fill-opacity": 1,
                   "fill-outline-color": "#ff0000"
               }
           };
           var usa_normal_line_layer = {
               'id': 'usa_normal_line_id',
               'type': 'line',
               'source': 'usa_tile_source',
               'source-layer': 'usa4326',
               'layout': {
                   'line-join': 'round',
                   'line-cap': 'round'
               },
               'paint': {
                   "line-color": "#0000ff",
                   "line-width": 1
               }
           };
          
           map.addSource('bjroad_tile_source', bjroad_tile_source);
           map.addSource('usa_tile_source', usa_tile_source);
           //map.addLayer(bjroad_polygon_layer);
           map.addLayer(bjroad_normal_line_layer);
           map.addLayer(usa_normal_line_layer);
       });
    </script>
    </body>
    </html>	

    打开上面的网页,可以得到如下所示的效果图:

     

            通过上述实验,我发现Mapbox前端会默认将mbtiles的数据由EPSG:4326转化为EPSG:3857(Web墨卡托),因此我们可以在生成矢量瓦片数据集时直接生成EPSG:3857的数据集,这样可以减少前端投影变化的步骤,提升响应效率。

    5.讨论

           在实验中我发现,本地生成的矢量瓦片数据集mbtiles似乎无法直接展示,而是需要在初始化Map时设定一个mapbox官方提供的底图服务作为baselayer,这一点需要后续进一步深挖与研究。

    参考文献

    [1]孙晨龙, 霍亮, 高泽辉.基于矢量瓦片的矢量数据组织方法研究[J].测绘与空间地理信息,2016,39(4):122-124.

    [2]陈举平,丁建勋.矢量瓦片地图关键技术研究[J].地理空间信息,2017,15(8):44-47.

    [3]Lin L, Wei H, Zhu H, et al. Tiled vector data modelfor the geographical features of symbolized maps[J]. Plos One, 2017, 12(5).

    [4]Wan L, Huang Z, Peng X. An Effective NoSQL-BasedVector Map Tile Management Approach[J].International Journal ofGeo-Information,2016,5(11):215.

    [5]Mapbox,Mapbox vectorTile Specification[EB/OL].https://www.mapbox.com/vector-tiles/specification,2016-12-25/2017-02-30.

    [6]王亚平,蒲英霞,刘大伟等.基于TileStache的多源投影矢量数据瓦片生成技术研究[J].地理信息世界,2015,22(1):77-81.

    [7]李静,朱秀丽,周治武.矢量瓦片在线制图的地图标注研究[J].地理信息世界,2017(2):100-102.

    [8]朱秀丽,周治武,李静,等.网络矢量地图瓦片技术研究[J].测绘通报,2016(11)106-109.

    [9]Shi N X, Wu X. METHOD OF CLIENT SIDE MAP RENDERINGWITH TILED VECTOR DATA: US, US 7734412 B2[P]. 2010.

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 下载mapbox矢量切片 1.这里推荐一个离线地图下载器: mapdownload: mapbox离线地图下载 2.运行程序:点击mapdownload64.exe,填写下载参数等信息。 参数中token可以在mapbox官网随便找个示例复制粘贴。 3.下载完毕...

    1.离线部署ArcGIS JS API

    如果使用ES module模式加载ArcGIS JS API,则可以跳过此步。

    ES module加载的具体操作请移步官网

    Install and set up | Overview | ArcGIS API for JavaScript 4.21 | ArcGIS DeveloperDocumentation site for ArcGIS API for JavaScript on ArcGIS for Developers.https://developers.arcgis.com/javascript/latest/install-and-set-up/

    这里介绍离线下载并部署ArcGIS JS API的方法:

    1.下载ArcGIS JS API,下载地址:

    Downloads | ArcGIS Developer

    2.配置tomcat以及JDK,具体方法参考:非常详细图文JDK和Tomcat安装和配置的图文教程_追梦赤子心-CSDN博客_tomcat配置jdk

    3.解压ArcGIS JS API安装包,将文件夹移至tomcat/webapp下,即可完成离线部署。

    2.下载mapbox矢量切片

    1.这里推荐一个离线地图下载器:

    mapdownload: mapbox离线地图下载

    2.运行程序:点击mapdownload64.exe,填写下载参数等信息。

     参数中token可以在mapbox官网随便找个示例复制粘贴。

    3.下载完毕之后将整个下载文件夹复制粘贴到tomcat/webapp下。

     4.几个文件夹:fonts为地图的字体文件,source中包含各个级别的矢量切片,sprites文件夹中为地图图标。

     3.示例代码

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no" />
      <title>Intro to MapView - Create a 2D map</title>
      <style>
        html,
        body,
        #viewDiv {
          padding: 0;
          margin: 0;
          height: 100%;
          width: 100%;
        }
      </style>
      <link rel="stylesheet"
        href="http://localhost:8181/arcgis_js_v421_api/arcgis_js_api/javascript/4.21/esri/themes/light/main.css" />
      <script src="http://localhost:8181/arcgis_js_v421_api/arcgis_js_api/javascript/4.21/init.js"></script>
      <script src="http://localhost:8181/jquery/jQueryDownload/jquery-3.1.1.min.js"></script>
    
    
      <script>
        require([
          "esri/Map",
          "esri/views/MapView",
          "esri/layers/WebTileLayer",
          "esri/layers/VectorTileLayer",
          "esri/layers/TileLayer",
        ], function (Map, MapView, WebTileLayer, VectorTileLayer, TileLayer) {
    
          // 封装ajax函数
          const fetch = (url) => {
            const p = new Promise((resolve, reject) => {
              $.ajax(url, {
                dataType: 'json',
                success: function (result) {
                  resolve(result);
                },
                error: function () {
                  reject(new Error('返回错误'))
                }
              })
            })
            return p
          }
    
    
          const queryStyleJson = async (url) => {
            try {
              const res = await fetch(url)
              return res
            } catch (e) {
              console.error(e)
            }
          }
    
          // 获取服务器上的style文件(这里以本机为例)
          queryStyleJson('http://localhost:8181/mapdownload/style.json').then((res) => {
            // console.log('dddd', res);
    
            // 设置矢量切片源
            res.sources.composite = {
              type: "vector",
              tiles: ["http://localhost:8181/mapdownload/source/composite/tiles/{z}/{x}/{y}.pbf"],
            }
            // 设置地图小图标
            res.sprite = "http://localhost:8181/mapdownload/sprites/sprite"
            // 设置字体文件
            res.glyphs = "http://localhost:8181/mapdownload/fonts/{fontstack}/{range}.pbf"
    
            let vtlLayer = new VectorTileLayer({ style: res });
            var map = new Map({
              basemap: { baseLayers: [vtlLayer] },
            });
            var view = new MapView({
              container: "viewDiv",
              map: map,
              zoom: 9,
              center: [117.5, 39.5],
            });
    
          })
    
    
        });
      </script>
    </head>
    
    <body>
      <div id="viewDiv"></div>
    </body>
    
    </html>

    4.断开网络进行离线加载测试

     发现在离线环境中地图,字体以及图标都能正确加载。

    整个部署过程完成!

    展开全文
  • 特塞拉 tessella是轻量级的Node.js 服务器。 受启发。 安装 tessella需要ES2015和更高版本的节点v7.6.0或更高版本才能支持异步功能。 npm install tessella -g npm install -g ......where [uri] is tilelive URI ...
  • mapbox矢量切片工具:tippecanoe

    千次阅读 2020-09-20 21:36:58
    矢量切片工具:tippecanoe Tippecanoe 用于将 GeoJSON, Geobuf, 或者 CSV 格式的矢量要素转换为矢量瓦片。 目的 Tippecanoe 的目的是将数据制作为比例独立的视图,以使在任何缩放级别下,你都可以看到数据的密度和...
  • 图1-1 苏州园区建筑地块数据二、geoserver 安装我在早之前安装过geoserver2.14.2版本和该版本对应的插件,使用该版本发布服务后遇到了pbf格式文件无法预览的问题(geojson格式倒是可以),其实在预览时切片文件夹下...
  • mapbox-gl主要的渲染方式是加载矢量切片(vectortiles),在前端根据自己设定的图层样式进行实时渲染,这和传统的通过后端制作好底图,发布成图片格式的切片是有区别的。在mapbox-gl的使用过程中,制作自己的矢量...
  • 矢量切片发布完成,需要在前端进行样式配置GIS开发:客户端控制的地图样式,颜色、字号、线宽、填充色、标签图片等属性设置,mapbox-gl样式配置结果是一个json文件,图层比较多时,需要一个可视化的工具进行编辑,...
  • 矢量切片数据制作完成后,在前端进行调用的时候,需要在服务器端进行数据发布,根据矢量切片不同的存在形式,发布的方式也不同。 分散的矢量切片数据,常用的web服务器软件就能够发布,例如Apache、nginx、tomcat等...
  • 这种方式有一个问题就是当你的数据库新添了一个表后你必须用geoserver发布一下才能使用它的矢量瓦片,因为业务需要,数据库中可能会动态添加进表,要动态的产生矢量服务,在此找到如下解决方案:一、服务端搭建:1....
  • mapbox添加pbf矢量切片图层
  • 矢量切片数量小,传输速度快,不受分辨率影响,可以动态调整样式,有着不可替代的优点。最近在研究如何使用Leaflet加载Mapbox studio生成的pbf格式的切片。主要用了两种方案。这里讲第一种,使用Leaflet.VectorGrid...
  • mapbox-vector-tiles 使用mapbox-gl渲染mapbox矢量图块格式
  • 最近在做关于mapbox的项目,看到这篇文章感觉特别好,特此转载记录一下。 先不说废话直接上地址:(所有东西都在阿里云的共享云虚拟主机上,访问地图可以会有点慢,请多多包涵)。 01:中国地图:...
  • 矢量切片(vector tile)是当前 WebGIS 较热技术,国内的高德、百度等在线...矢量切片格式一般有 GeoJSON、TopoJSON 、MVT (MapBox Vector Tile)、PBF。下面是 Cesium 加载 MVT 矢量切片的代码,由于样式渲染使用的...
  • 这会添加一个 Canvas 元素并将其覆盖在 mapbox-gl 地图之上。 热图将在地图状态更改时重置。 用法:var mapboxgl_heatmap = require('mapboxgl-heatmap');var heatmap;function msgRec(msg) { var latlng = new ...
  • mapbox map.addSource('vector', { //此处的vector是我们的source的id type: 'vector', // 此处vector即为矢量瓦片的类型 tiles: [ '你的地址,地址末尾应该是这样的{z}/{x}/{y}.vector.pbf' ], tileSize: 512...
  • mapbox本地化vue调用(矢量切片) 要求搞这个mapbox的离线部署,走了好些弯路,一开始没搞明白光栅切片和矢量切片的区别,就开始搞光栅切片。瓦片就是一级一级的,放得越大,级数越大,每一级的切片也就越多。 光栅...
  • 最近在做关于mapbox的项目,看到这篇文章感觉特别好,特此转载记录一下。 先不说废话直接上地址:(所有东西都在阿里云的共享云虚拟主机上,访问地图可以会有点慢,请多多包涵)。 01:中国地图:...
  • 发布矢量图层 1.发布图层的时候勾选如下 注意:发布的图层必须有坐标系 三.代码调用 < template > < div > < div id = "mapDiv" > < / div > < / div > < / template > < script > import ...
  • python3下载mapbox矢量切片通过观察mapbox的页面开发者工具里的network可以发现,打开矢量切片和字体切片pbf和prite图标的链接,即可下载文件。所以写了个python程序不断请求mapbox的页面,下载矢量切片。用同样的...
  • 有关矢量切片以及MVT(MapBox Vector Tile)自行搜索了解,不再赘述。 接口格式 http://domain:port/map/vector/tile/{z}/{x}/{y},其中z代表缩放层级,x、y代表当前行列号 一、方法 采用postgis的函数ST_AsMVT、ST...
  • geoserver发布矢量切片

    2021-12-21 09:03:21
    参考:矢量切片 下载:切片Vector Tiles插件 解压缩后将里面的jar包拷贝到E:\apache-tomcat-9.0.31-8080\webapps\geoserver\WEB-INF\lib,重启geoserver即可 编辑已有的图层会在Tile Caching看到多出来的几个选项...
  • Mapbox GL 加载GeoServer发布的矢量切片

    千次阅读 2021-02-20 20:40:15
    Mapbox GL 加载单个图层的矢量切片3. 图层组矢量切片发布与Mapbox GL 加载 1. GeoServer发布单个图层的矢量切片 1.1 安装插件 下载插件 geoserver-2.13.3-vectortiles-plugin(需查找当前对应的GeoServer版本) ...
  • Mapbox4490版,可以加载CGCS2000(国家)坐标系数据源。但必须整个配图里的数据源都是4490坐标系。直接替换原来的Mapbox库即可使用

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 576
精华内容 230
关键字:

mapbox矢量切片

友情链接: 数字排序.zip