Skip to content
目录

VectorTileLayer

VectorTileLayer矢量瓦片图层,是个比较复杂的图层,其复杂性主要体现在样式的复杂

maptalks团队也推出了个对应的产品 MapTalks Designer 来解决矢量切片样式配置复杂的问题

关于改图层需要你认真的阅读官方的文档

注意其在@maptalks/gl-layers包里,你需要:

sh
npm i @maptalks/gl-layers

关于矢量切片

矢量切片是mapbox公司推出的全新的瓦片切图技术,其主要包括:

  • 传统的切图,都是切成图片的(tile用TileLayer来加载),矢量切片是把一个大的数据切成一个个瓦片数据(VectorTile)
  • 针对VectorTile制定了一套样式系统,注意这个样式系统比较复杂,需要花大量的时间来学习的,不是随随便便花点时间就能吃透的
  • 前端引擎请求VectorTile,加上样式系统然后在前端动态的渲染出整个地图

优点

  • 地图样式不在千篇一律,样式随意定制,可以用来做各种个性化样式定制,一套数据可以在不同的样式下表达出不同风格的地图
  • 因为整个渲染是在前端完成的,整个地图效果显得比较精致,尤其是在三维下,文字的渲染效果比用传统的栅格瓦片要好的不是一点点
  • 前端可以直接完成数据的交互,而栅格瓦片是图片,是无法交互的

缺点

  • 因为整个渲染流程全部在前端完成,对前端压力比较大,如果你的业务里有大量的业务图层,会导致整个系统卡顿甚至崩溃
  • 相对于传统的栅格瓦片,其请求的是数据,会加大浏览器网络压力,可能对你的业务里非GIS网络请求造成挤压
  • 兼容性相对于栅格瓦片比较差,老旧的浏览器不支持

PBF VS MVT

网上有很多pbf和mvt的文章,我个人认为没有一个说的到位的

  • pbf 谷歌推出的一种数据格式,特点是体积小,传输速度快,缺点也很明显,二进制的可读性差,相对于json等灵活性差
  • mvt全称:Mapbox Vector Tile是一个标准,现在也是OGC的标准,和pbf完全是两个不同的东西

pbf和mvt的关系

mapbox mvt切的数据采用pbf这个格式存储,仅仅而已,再也没有其他的关系了,mapbox为么么要这样做呢?

  • 瓦片请求是个高频场景,一个页面里会有大量的瓦片请求,pbf的体积小,传输速度快这时就显得很应景了
  • mapbox是全球化企业,其地图服务是全球的,数据体积本来就大,采用pbf可以减少存储体积和成本
  • mapbox的数据服务是托管在AWS上的,采用pbf每年省下来的存储空间和网络流量也是一笔非常可观的成本了
  • mapbox早期服务的后缀名为.mvt后来改为.pbf了,至于为什么我也不太清楚

包括google搞pbf本身也是因为成本的原因,假设pbf相对于json能省一半的数据体积,对google这种巨大流量公司的来说,一年省下的流量成本那可是一大笔钱的

为什么大家会将pbf和mvt划等号?

  • 矢量切片是mapbox公司推出的,且默认采用了pbf存储,导致大家形成了这种惯性思维
  • 绝大数公司没有实现mapbox规范的能力,都是默认采用mapbox的默认实现的数据格式

其实针对mvt思想实现的的数据格式不一定是pbf,也可以是其他的格式的:

  • geoserver支持输出geojson
  • mapzen支持输出geojson,topojson
  • 包括国内的的百度高德等也是采用MVT的思想,自己实现的格式和样式,但是他们都不是用的mapbox那一套,他们都是自定义格式的

总结

  • mapbox mvt切的数据采用pbf这个格式存储,仅仅而已,再也没有其他的关系了

maptalks里的矢量切片

maptalks里的矢量切片和mapbox规范还是有不少区别的.

相同点

  • 数据格式方面采用了和mapbox一样的pbf和一样的数据结构(我目前看到是这样,至于后期是否推出自己的数据格式就不知道了)

不同点

  • maptalks里有自己的样式系统和mapbox的不兼容的
  • maptalks的样式系统里还包括了材质,PBR等这些专业的图形东西(反正我也不懂这些是是么),渲染出来的东西比较nice
  • maptalks的vt支持3d模型样式系统

常见问题

关于样式

  • maptalks有自己的样式系统,和mapbox的样式系统不是一会事情,如果想把mapbox的样式应用到maptalks 里需要一个转换工具,目前有没有现成的工具我也不知道
  • VectorTileLayer的style属性是个数组,里面可以包含任意个数的渲染插件
    • 一个渲染插件就是一个子图层(组)
    • 其数据利用feature-filter 从 VectorTileLayer这个大图层里筛选出来的
js
const style = {
    style: [
        //一个子图层
        {
            //the style item name,Its value should be unique
            name: "building",
            //从VectorTileLayer里筛选数据
            //https://doc.maptalks.com/docs/style/filter/feature-filter/
            filter: [
                "all",
                ["==", "$layer", "building"],
                ["==", "$type", "Polygon"],
            ],
            //利用哪个渲染插件来渲染这些数据
            // https://doc.maptalks.com/docs/style/plugin-icon/
            renderPlugin: {
                dataConfig: {
                    type: "fill",
                },
                type: "fill",
            },
            //样式
            //https://doc.maptalks.com/docs/style/symbols/
            symbol: {
                polygonFill: "#2e7e57",
                polygonOpacity: 1,
            },
        },
        //other item,其他的子图层,个数不限制
    ],
};
  • 注意同一条数据可以被多个子图层筛选到的,所有子图层的数据量之和完全可以>总图层里的数据量之和的

maxAvailableZoom

VectorTileLayer是基于TileLayer的,和TileLayer一样,有个参数叫maxAvailableZoom,用来设置图层的可见最大层级,当设置了改参数后,地图的层级超过这个值时会复用你设置的这个参数层级的数据,比如你设置了18,当地图放大道19时仍然会复用18层级的数据

js
const vt = new maptalks.VectorTileLayer("vt", {
    urlTemplate: "http://tile.maptalks.com/test/planet-single/{z}/{x}/{y}.mvt",
    features: true,
    pickingGeometry: true,
    maxAvailableZoom: 18,
    style,
});

提示数据格式不能被解析

这种报错一般都是服务端返回的数据不是正确的的mvt格式导致的

  • 检查页面的网络请求,看看请求的数据服务端返回的是否是mvt
  • 如果使用GeoServer,GeoServer返回的数据经常是xml格式的,一般都是报错的返回的 结果,比如瓦片请求超过改图层配置的边界返回了xml,可以把你的图层的边界返回设置成全球的范围

更新图层的整体样式

js
layer.setStyle(style);

更新一个组的样式

js
const style = {
    style: [
        {
            //the style item name,Its value should be unique
            name: "building",
            filter: [
                "all",
                ["==", "$layer", "building"],
                ["==", "$type", "Polygon"],
            ],
            renderPlugin: {
                dataConfig: {
                    type: "fill",
                },
                type: "fill",
            },
            symbol: {
                polygonFill: "#2e7e57",
                polygonOpacity: 1,
            },
        },
    ],
};
layer.updateSymbol("building", {
    polygonFill: "red",
});

WARNING

updateSymbol支持用数组下标索引来更新样式,但是不建议这么做,请为每个样式组起个名字,使用名字来更新样式

  • 索引的方式不好容易出bug,如果用户对样式做任何的排序和增删改等都会导致样式错乱更新
  • 使用名字可以避免这些问题
  • 每个样式组的名字要做的唯一性
  • 样式组的名字不做强制要求,但是推荐为每个样式组起个名字,比如building,road
  • 代码里不要使用 updateSymbol(0,style)这样的方式更新样式,否则当你对样式做任何排序和增删改等都会导致样式错乱更新

高亮元素的样式

高亮的操作方法有:

  • highlight
  • cancelHighlight
js
layer.highlight([
    {
        name: getHighLightKey("fill"),
        id: 12,
        plugin: "area-fill", //only effect 'area-fill' render plugin
        ...params,
    },
    {
        name: getHighLightKey("border"),
        id: 12,
        plugin: "area-border",
        color: params.lineColor,
    },
]);
  • id 元素的id

  • name 表示 本次高亮的key,要做到唯一性,多个name相同会进行覆盖的,取消高亮操作时 要需要这个name的值的

  • plugin表示作用那个插件,否则会导致样式里的所有样式组的元素都被高亮,比如一条道路可能被多个渲染插件 渲染,这时指定了渲染插件后就只会高亮这个渲染插件里的该条元素,支持数组

支持用filter来批量高亮元素集合

js
layer.highlight([
    {
        name: getHighLightKey("fill"),
        plugin: "area-fill", //only effect 'area-fill' render plugin
        filter: (feature) => {
            const name = feature.properties.name;
            return name && name.includes("");
        },
        ...params,
    },
    {
        name: getHighLightKey("border"),
        plugin: "area-border",
        filter: (feature) => {
            const name = feature.properties.name;
            return name && name.includes("");
        },
        color: params.lineColor,
    },
]);
//cancel highlight
layer.cancelHighlight([getHighLightKey("fill"), getHighLightKey("border")]);

高亮元素

高亮元素-filter

WARNING

highlight只能改变元素的颜色,透明度,显示与隐藏,bloom等这些基本样式,无法改变元素的渲染形态,比如将一个2d的元素 渲染成3d

获取图层里的要素的具体信息

js
const data = layer.identify(e.coordinate);
if (!data || !data.length) {
    return;
}

WARNING

要想获取图形的完整的信息需要把图层的features打开

js
const vt = new maptalks.VectorTileLayer("vt", {
    urlTemplate: "http://tile.maptalks.com/test/planet-single/{z}/{x}/{y}.mvt",
    features: true,
    pickingGeometry: true,
    style,
});

设置样式组的zoom filter

样式里可以配置样式组的minZoommaxZoom的,这个例子演示了将样式组的zoom区间限制 在[8,12]

js
const style = {
    style: [
        {
            filter: true,
            renderPlugin: {
                dataConfig: {
                    type: "fill",
                },
                sceneConfig: {
                    minZoom: 8,
                    maxZoom: 12,
                },
                type: "fill",
            },
            symbol: {
                polygonFill: "#89c2be",
                polygonOpacity: 1,
            },
        },
        {
            filter: true,
            renderPlugin: {
                dataConfig: {
                    type: "line",
                },
                sceneConfig: {
                    minZoom: 8,
                    maxZoom: 12,
                },
                type: "line",
            },
            symbol: {
                lineColor: "#E2E2E2",
                lineOpacity: 1,
                lineWidth: 2,
            },
        },
    ],
    featureStyle: [
        {
            id: 12,
            style: [
                {
                    renderPlugin: {
                        dataConfig: {
                            type: "fill",
                        },
                        sceneConfig: {},
                        type: "fill",
                    },
                    symbol: {
                        polygonFill: "#ef9e9f",
                        polygonOpacity: 1,
                    },
                },
                {
                    renderPlugin: {
                        dataConfig: {
                            type: "line",
                        },
                        sceneConfig: {},
                        type: "line",
                    },
                    symbol: {
                        lineColor: "#CB7575",
                        lineWidth: 2,
                    },
                },
            ],
        },
    ],
};

怎样判断某个元素是否被加载?

因为矢量切片数据加载是异步的(网络请求),且只加载当前视野内的数据,所以我们无法主动的去判断 某条数据是否被加载和渲染了,因此只能被动的去判断该条数据是否被加载了

  • 监听图层的 tileload事件
  • 获取当前的所有的渲染数据getRenderedFeatures
  • 如果所有的数据里包含了这条数据那么就可以确认改条数据被加载了

WARNING

因为矢量瓦片会对要素进行网格切片,所有无法判断一条数据是否被完整的的加载的,比如一个湖泊的,可能被切片 分散到多个瓦片里,所以只要任何一个瓦片里包含这条数据我们就认为其被加载了

js
function hightLightFeature(id) {
    layer.highlight([
        {
            name: "test" + id,
            id,
            bloom: true,
            color: "red",
            plugin: "landuse-fill", //only effect 'landuse-fill' render plugin
            // ...params,
        },
        // {
        //     name: getHighLightKey('border'),
        //     id: 12,
        //     plugin: 'area-border',
        //     color: params.lineColor
        // }
    ]);
}

// layer.on("dataload", (e) => {
//     map.fitExtent(e.extent);
// });

//需要高亮的业务数据ID
const osm_ids = ["53511131", "461440696"];
//已经高亮的数据缓存
// const hightLightMap = new Map();

layer.on("tileload", (e) => {
    const layer = e.target;
    //获取当前的所有的加载和渲染的要素集合
    const tiles = layer.getRenderedFeatures();
    osm_ids.forEach((osmid) => {
        // if (hightLightMap.has(osmid)) {
        //     return;
        // }
        for (let i = 0, len = tiles.length; i < len; i++) {
            const features = tiles[i].features;
            let isHit = false;
            for (let j = 0, len1 = features.length; j < len1; j++) {
                const feature = features[j].feature;
                if (feature.properties.osm_id === osmid) {
                    isHit = true;
                    hightLightFeature(feature.id);
                    break;
                }
            }
            if (isHit) {
                break;
            }
        }
    });
});

怎样高效的批量高亮元素

VectorTileLayer 默认提供了filter函数来批量高亮元素 highlight-filter 但是有的同学反应性能不太好

性能不好的原因:

  • 图层内的任何数据的更新都需要用filter函数跑一遍来确定哪些数据需要高亮
  • filter function callback相对于for循环性能本就比较差,这个是js语法的决定
  • 可能VectorTileLayer内部实现的有点问题

批量高亮的方法有:

  • 利用filter,但是filter可能性能比较差,因为filter本质就是模糊匹配了,图层内任何数据的更新都 需要跑一遍filter函数,因为图层无法确认哪些数据需要高亮,只有用filter把所有的数据跑一遍才知道

  • 从服务端查询需要高亮数据的id集合,建立高亮数据的集合,用id集合批量高亮,之所以要从 服务端查询,是因为vt只能加载当前视野内的数据,无法从图层内拿到所有的数据集合,故而不能拿到所有需要高亮数据的集合

  • 前端数据收集,监听瓦片的加载完事件,建立高亮数据的集合,不断的收集瓦片的数据,满足条件的数据建立一个数据集,随着瓦片的不断的加载这个高亮数据集合会越来越接近你的需要的集合

方法三这里我们称之为 被动数据收集法

下面我们利用方法三来演示个例子:

  • 数据的名字的长度>7 的元素进行高亮

  • 监听瓦片加载完成事件进行我们需要的数据收集

js
//已经高亮的数据缓存
const featureIdCache = new Map();

layer.on("tileload", (e) => {
    const layer = e.target;
    //获取当前的所有的加载和渲染的要素集合
    const tiles = layer.getRenderedFeatures();
    for (let i = 0, len = tiles.length; i < len; i++) {
        const features = tiles[i].features;
        for (let j = 0, len1 = features.length; j < len1; j++) {
            const feature = features[j].feature;
            const id = feature.id;
            //名字长度大于7的才符合我们的需求
            const name = feature.properties.name;
            if (!name || name.length < 7) {
                continue;
            }
            if (featureIdCache.has(id)) {
                continue;
            }
            highData.push({
                high: false,
                id,
                feature,
            });
            featureIdCache.set(id, 1);
        }
    }
    hightLightFeature();
});
  • 高亮没有被高亮过的数据要素
js
const highData = [];

function hightLightFeature() {
    if (!highData.length) {
        return;
    }
    const needHighLights = highData
        .filter((data) => {
            return !data.high;
        })
        .map((data) => {
            data.high = true;
            return {
                name: `test-${data.id}`,
                id: data.id,
                bloom: true,
                color: "red",
                plugin: ["landuse-fill"], //only effect 'landuse-fill' render plugin
            };
        });
    layer.highlight(needHighLights);
}

TIP

这里只是简单的演示批量高亮的方法三,业务根据自己的需要收集自己的业务数据即可

怎样查看切片内数据的内容和结构

图层创建是有个debugTileData配置选项,开启后加载切片数据的过程中会在控制台打印每个切片里的数据的

js
const layer = new maptalks.VectorTileLayer("id", {
    debugTileData: true,
    //others config
});

WARNING

记得生产环境下不要开启改选项

  • 避免数据泄漏
  • 此选项性能比较差,主要应用场景就是开发是调试用的

同一个数据出现多个标注数据

这个是有矢量切片性质决定的,当一个大的图形数据在高层及下会被切割成多个瓦片,导致每个瓦片都出现了图形 的一部分数据,从而导致每个瓦片内都出现了图形的标注了

解决方法:
做一个独立的标注的数据集(Point数据集合),在vt样式里配置这个独立的数据集合的样式

  • 这个是矢量切片里针对标注的推荐做法
  • 因为点在任何缩放层级下都只能落在一个瓦片内,所以可以避免这个问题
  • 用独立的标注图层,也可以做到自定义标注的位置更加美观和友好,上图这种标注都是根据每个切片的数据利用 数学方法自动生成的,具有随机性,可能不符合实际情况,比如江苏省这样的一个行政区,我们标注一般都是标注在 省会南京,这样的需求就只能自己专门做一个标注图层数据集了

可以加载百度高德等的矢量切片服务吗?

不可以,因为百度高德等是商业公司,他们的数据格式都是加密和私有的,不对外开放的,如果要强制加载就不得不破解他们的 数据,这样会触发法律问题.

  • 作为一个公开的引擎不能这么干,用户量大了会引起法律问题
  • 至于个人去破解等,这些属于用户的个人行为,相关法律问题有个人自己承担

怎样加载GeoServer发布的矢量切片?

GeoServer的安装和图层服务发布

  • 安装GeoServer,怎样建立自己的工作区和上传图层数据具体的请网上查阅相关文章资料 GeoServer官方教程:矢量切片

  • 按照上面的教程做好了后,在GeoServer的瓦片缓存目录里查看自己刚才新建的图层服务是否成功

  • 预览里选择刚才你自己新建的瓦片服务,通过预览以此来查看服务是否发布成功,这里我选择了 EPSG:900913/pbf

TIP

EPSG:900913 和我们平时说的EPSG:3857是相等的,就是google标准的墨卡托

Markdown 官方教程

如果看到这样的显示效果就证明我们的服务发布成功了

Markdown 官方教程

maptalks来加载服务

  • 在GeoServer预览的页面里,我们可以通过网络请求来抓捕服务请求的地址规律,GeoServer的请求规则我也不太懂, 所以只能结果论了

Markdown 官方教程Markdown 官方教程

js
const url = `
http://localhost:8080/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:900913:7&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL=105&TILEROW=52
`;

从地址里可以看到有几个参数比较特殊:

  1. TILEMATRIX=EPSG:900913:7 里面包含了投影信息和地图缩放层级Zoom,我们把其改成TILEMATRIX=EPSG:900913:{z}
  2. TILEMATRIXSET: EPSG:900913 不用动
  3. TILECOL=105 瓦片的请求的列,即{x}
  4. TILEROW=52 瓦片请求的行号即 {y}

INFO

懂wmts规范的同学的应该能看出来了这就是一个wmts服务请求的参数

这样就我们就可以得到一个新的地址:

js
const url = `
                http://localhost/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}
                `;
  • 准备maptalks代码
js
const map = new maptalks.Map("map", {
    center: [120.58443009853363, 31.077306186678225],
    zoom: 5,
    bearing: 0,
    pitch: 0,
    centerCross: true,
    // spatialReference: {
    //     projection: 'EPSG:4326'
    // },
    baseLayer: new maptalks.TileLayer("base", {
        // debug: true,
        urlTemplate:
            "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
        subdomains: ["a", "b", "c", "d"],

        spatialReference: {
            projection: "EPSG:3857",
        },
    }),
});

map.on("click", (e) => {
    console.log(e.coordinate.toArray());
});

const groupGLLayer = new maptalks.GroupGLLayer("gl", [], {}).addTo(map);

const url = `
                http://localhost:8080/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}
                `;

const vtLayer = new maptalks.VectorTileLayer("vt", {
    debug: true,
    urlTemplate: url,
});
groupGLLayer.addLayer(vtLayer);

Markdown 官方教程

加载4326的服务

从上面的介绍里,怎样拿EPSG:4326服务的地址应该就不用我介绍了吧.

我们拿到这样的地址:

js
const url = `
http://localhost:8080/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:4326:7&TILEMATRIXSET=EPSG:4326&FORMAT=application/vnd.mapbox-vector-tile&TILECOL=212&TILEROW=40
`;

然后改成:

js
const url = `
                http://localhost:8080/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:4326:{z}&TILEMATRIXSET=EPSG:4326&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}
                `;
  • 准备maptalks代码
js
const map = new maptalks.Map("map", {
    center: [120.58443009853363, 31.077306186678225],
    zoom: 5,
    bearing: 0,
    pitch: 0,
    centerCross: true,
    spatialReference: {
        projection: "EPSG:4326",
    },
    baseLayer: new maptalks.TileLayer("base", {
        // debug: true,
        urlTemplate:
            "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
        subdomains: ["a", "b", "c", "d"],

        spatialReference: {
            projection: "EPSG:3857",
        },
    }),
});

map.on("click", (e) => {
    console.log(e.coordinate.toArray());
});

const groupGLLayer = new maptalks.GroupGLLayer("gl", [], {}).addTo(map);
const url = `
                http://localhost:8080/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:4326:{z}&TILEMATRIXSET=EPSG:4326&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}
                `;

const vtLayer = new maptalks.VectorTileLayer("vt", {
    debug: true,
    urlTemplate: url,
});
groupGLLayer.addLayer(vtLayer);

WARNING

注意:

  1. 地图的投影必须设置成4326,即上面的:
js
spatialReference: {
    projection: "EPSG:4326";
}
  1. VectorTileLayer 不要设置投影信息,其会自动从地图上继承的,不要手动设置其为4326,矢量瓦片的4326和我平时的 瓦片还有点不同的,maptalks内部做自动做计算的(会自动省去前几个级别的切图参数),不用去管,如果你手动设置了会导致 服务加载不出来,至于为什么我暂时还没有搞清楚

Markdown 官方教程

注意事项

GeoServer默认是没有开启跨域选项,所以在访问服务时会遇到跨域报错,解决方法:

  • GeoServer服务开启跨域服务,具体的请网上搜索对应的文章
  • 自己前端开启个代理服务,然后把瓦片服务的请求地址改成自己的代理地址

完整代码

js
const map = new maptalks.Map("map", {
    center: [120.58443009853363, 31.077306186678225],
    zoom: 4,
    bearing: 0,
    pitch: 0,
    centerCross: true,
    // spatialReference: {
    //     projection: 'EPSG:4326'
    // },
    baseLayer: new maptalks.TileLayer("base", {
        // debug: true,
        urlTemplate:
            "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
        subdomains: ["a", "b", "c", "d"],

        spatialReference: {
            projection: "EPSG:3857",
        },
    }),
});

map.on("click", (e) => {
    console.log(e.coordinate.toArray());
});

const groupGLLayer = new maptalks.GroupGLLayer("gl", [], {}).addTo(map);

function test4326() {
    map.setSpatialReference({
        projection: "EPSG:4326",
    });
    const url = `
                http://localhost/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:4326:{z}&TILEMATRIXSET=EPSG:4326&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}
                `;

    const vtLayer = new maptalks.VectorTileLayer("vt", {
        debug: true,
        urlTemplate: url,
    });
    groupGLLayer.addLayer(vtLayer);
}

function test3857() {
    const url = `
                http://localhost/geoserver/deyihu/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=deyihu:jiangsu-area&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}
                `;

    const vtLayer = new maptalks.VectorTileLayer("vt", {
        debug: true,
        urlTemplate: url,
    });
    groupGLLayer.addLayer(vtLayer);
}

test4326();

加载supermap iserver 发布的4326服务

超图发布的矢量瓦片服务经过我的验证就是常规的4326服务,经过我的测试其瓦片大小是256的, 并不是我们常见的:一般矢量瓦片都是512的

WARNING

本人只是猜测,对超图的东西并没有深入研究过

js
const map = new maptalks.Map("map", {
    center: [-122.2543440112645, 38.236059513982674],
    zoom: 13,
    bearing: 0,
    pitch: 0,
    centerCross: true,
    zoomControl: true,
    spatialReference: {
        projection: "EPSG:4326",
    },
});

const groupGLLayer = new maptalks.GroupGLLayer("gl", [], {}).addTo(map);
// https://iclient.supermap.io/examples/mapboxgl/editor.html#mvtVectorTile_4326
const url = `https://iserver.supermap.io/iserver/services/map-mvt-California/rest/maps/California/tileFeature.mvt?returnAttributes=true&width=512&height=512&x={x}&y={y}&z={z}`;

const vtLayer = new maptalks.VectorTileLayer("vt", {
    debug: true,
    urlTemplate: url,
    tileSize: 256,
    spatialReference: {
        projection: "EPSG:4326",
    },
});
groupGLLayer.addLayer(vtLayer);

矢量瓦片图层和普通图层的区别和定位

矢量瓦片(VectorTileLayer)图层的特点:

  • 仅仅加载当前视野的数据(本质就是分页),不同的层级下加载不同精细度的数据(LOD)
  • 适用加载大规模的数据(城市底图,城市建筑物道路等)
  • 不适合细粒度细的场景
    • 编辑
    • 拖拽
    • 动画
    • 部分数据更新等
    • 数据频繁的更新
  • 无法拿到图层内的所有数据,只能拿到当前视野内加载的数据

TIP

如果你的数据量大且数据是静态的即数据侧重展示这时就非常适合矢量瓦片了

图形管理类的图层(PointLayer,LineStringLayer,PolygonLayer等)的特点:

  • 适用加载普通规模的数据
  • 适合细粒度细的场景
    • 编辑
    • 拖拽
    • 动画
    • 部分数据更新等
    • 数据频繁的更新
  • 可以拿到图层内的所有数据,可以对数据做精细化的管理,可以管理到图层内的每条数据

TIP

如果你的数据侧重生产力,即大量的编辑,大量的修改等这种类似生产力的就适合这种图形管理类的图层了

WARNING

所以在选择图层时要注意根据场景选择合适的图层,不可滥用.

矢量瓦片和图形管理类的技术问题:

  • 底层的渲染都是一样的
  • 能加载的最大数据量都是一样的,和用是么图层无关,取决于浏览器和硬件的配置
  • 矢量瓦片能加载大规模的数据主要在其只加载当前视野内的数据,还有就是LOD(Level Of Details)可以理解成前端的虚拟滚动
  • 图形管理图层不适合加载大规模的数据是因为我们是一股脑的加载所有数据,这数据规模超过了浏览器的承载范围而已, 如果你能做好其数据的调度能力也可以做到矢量瓦片那样的额大规模加载,不过等你把这些做好本质就是做了个矢量瓦片的数据调度系统

This document is generated by mdpress