Skip to content
目录

常见问题

渲染polygon数据出现漏洞

maptalks内默认会对图形进行simplify,这个问题是由于图形节点被简化导致的

你可以根据自己的需要设置简化的阈值

js
const polygon = new maptalks.Polygon(coordinates, {
    simplifyTolerance: 2,
});
//update options
// polygon.config({
//     simplifyTolerance: 1,
// });

simplifyTolerance的值越大,简化的越狠,性能也越好,根据自己的需要设置合适值即可,默认值2,

如果你的数据渲染出现这种空洞, 可以将其设置为1试试 当然你可以简单的粗暴的你设置图形不要进行simplify

js
const polygon = new maptalks.Polygon(coordinates, {
    enableSimplify: false,
});

//update options
// polygon.config({
//     enableSimplify: false,
// });

GLTF模型透明度的问题

shu (1).png

可以自己手动设置下 alphaTest

js
const symbol = {
    url: "/resources/gltf/alien/alien.gltf",
    shader: "pbr",
    rotationZ: 180,
    modelHeight: 240,
    uniforms: {
        alphaTest: 0.1,
    },
};

// symbol.uniforms.alphaTest = 0.1;

GLTF模型线框效果

渲染模式切换到wireframe即可

js
const point = new maptalks.GLTFMarker(center, {
    symbol: {
        url: "./../assets/data/rvE-hN_HeOPfqWbvA7ASi.glb",
        modelHeight: 50, //model height,Unit is meters
        scaleX: 1,
        scaleY: 1,
        scaleZ: 1,
        rotationZ: 180,
        shader: "wireframe",
    },
});

如果想模型和线框同时显示,需要加两条同样的数据,只是渲染模式不同而已

js
const point = new maptalks.GLTFMarker(center, {
    symbol: {
        url: "./../assets/data/rvE-hN_HeOPfqWbvA7ASi.glb",
        modelHeight: 50, //model height,Unit is meters
        scaleX: 1,
        scaleY: 1,
        scaleZ: 1,
        rotationZ: 180,
    },
});
const point1 = point.copy();
point1.updateSymbol({
    shader: "wireframe",
});

怎样弄个带海的图标效果

从效果图上看,使用单一的图形(Geometry)是无法做出这个效果,所以我们需要用多个图形来组成这个整体效果

  • 垂直的海拔线我们用 LineString来构造垂直的线
  • 构造一个带海拔的Marker
js
function addMarkers() {
    data.features.forEach((f) => {
        //随机海拔值
        f.geometry.coordinates.push(Math.random() * 200 + 200);
    });
    const markers = maptalks.GeoJSON.toGeometry(data);
    layer.addGeometry(markers);
}

function addLines() {
    const lines = data.features.map((f) => {
        //顶部坐标
        const coordinates = f.geometry.coordinates;
        //底部坐标
        const bottomCoordinates = [...coordinates].slice(0, 2);
        const line = new maptalks.LineString([bottomCoordinates, coordinates], {
            //海拔线只是个效果,我们默认关闭事件交互可以有更好的性能
            interactive: false,
        });
        return line;
    });
    layer.addGeometry(lines);
}

addMarkers();
addLines();

WARNING

如果使用 VectorLayer要开启海拔选项

js
const layer = new maptalks.VectorLayer("layer", {
    enableAltitude: true,
}).addTo(map);

GeoJSON数据怎样反序列化?

GIS项目会有大量的GeoJSON数据,将GeoJSON数据反序列化成maptalks的图形要素,看到不少同学都是用自己的 手动代码去反序列化的

不建议这么去做的,因为容易出错,尤其是对图形类型的判断,已经有好多同学在这里遇到问题了,最后发现问题都是出在 数据类型的判断上

GeoJSON.toGeometry

建议使用maptalks自带的GeoJSON.toGeometry反序列化工具

js
const geos = maptalks.GeoJSON.toGeometry(geojson);

如果需要对图形做一些处理,比如样式啊,配置啊,对数据进行分类啊,可以自己遍历图形要素进行各种设置即可

js
const geos = maptalks.GeoJSON.toGeometry(geojson);
geos.forEach((geo) => {
    //do some things setSymbol config,bind events etc
});

WARNING

注意

js
const geos = maptalks.GeoJSON.toGeometry(geojson);

的结果可能是 Geometry或者Array<Geometry>,不要直接使用geometry.addTo(layer)方法,因为Array上是没有 addTo方法的,会报错的,应该使用

js
layer.addGeometry(geos);

GeoJSON.toGeometryAsync

GeoJSON.toGeometry 的异步版本,利用微任务解决大量GeoJSON反序列化卡的问题,其返回的是一个Promise,如果你的 GeoJSON数据比较大且卡主线程了,你可以使用它

js
const time = "parse geojson";
console.time(time);
let index = 0;
console.log("parse geojson start");
maptalks.GeoJSON.toGeometryAsync(geojson, (geo) => {
    // geo.options.cursor = 'zoom-in';
    geo.setId(index);
    geo.setSymbol({
        polygonFill: "green",
        lineWidth: 0,
    });
    geo.on("mousemove", (e) => {
        console.log(e.target.getId());
    });
    index++;
}).then((geos) => {
    console.timeEnd(time);
    console.log("parse geojson end");
    layer.addGeometry(geos);
    console.log("geomtries.length", layer.getCount());
});

WARNING

该特性要求maptalks版本 version>=v1.0.0-rc.29

怎样让图形在特定的地图层级范围内显示

maptalks的图层提供了minZoommaxZoom配置选项来控制图层的层级显示范围,

js
const layer = new maptalks.VectorLayer("layer", {
    minZoom: 15,
    maxZoom: 18,
});

Geometry没有提供这样的设置,但是Geometry的样式里提供了visibleopacity等来控制样式的显示隐藏等,且支持function-type, 我们可以利用function-type来模拟minZoom,maxZoom的效果

假设有个Geometry想控制其

js
{

    minZoom:15,
    maxZoom:18
}

因为在[minZoom,maxZoom]区间的图形的可见性是一样的,这个很显然就适合用function-typeinterval来控制和模拟,因为interval一个区间内输出的值是同一个

js
const point = new maptalks.GLTFMarker(center, {
    symbol: {
        url: "./../assets/data/alien.gltf",
        modelHeight: 50, //model height,Unit is meters
        scaleX: 1,
        scaleY: 1,
        scaleZ: 1,
        rotationZ: 180,
        visible: {
            //mock minZoom=15,maxZoom=18
            stops: [
                [1, false],
                [15.0, true],
                [18.1, false],
                [Infinity, false],
            ],
            type: "interval",
        },
    },
});
  • [0,15)区间内显然要隐藏Geometry
  • [15,18]区间内要显示Geometry
  • (18,Infinity)区间内要隐藏Geometry

编辑图形比较卡

因为编辑图形时是为每个顶点都创建一个控制点,且每个线段还要创建一个新建点,故而编辑点的数量为 2n-2,当图形的顶点数据比较多时,就会导致编辑的控制点非常多

你可以在开启编辑时开启碰撞检测,来减少当前点的密集度,从而有更好的性能表现

js
polygon.startEdit({
    collision: true,
});

WARNING

该特性要求核心库 >=v1.0.0-rc.33

GeometryCollection

GeometryCollection 顾名思义表示Geometry集合的意思,用来批量管理Geometry

  • MultiPoint
  • MultiLineString
  • MultiPolygon

都是GeometryCollection的子类,在使用GeometryCollection时不要套娃,即不要将GeometryCollection自身和其子类作为 其子元素加入,否则可能会发生一些未知错误

js
const multiPoint = new maptalks.MultiPoint([]);
const geoCollection = new maptalks.GeometryCollection([multiPoint]);

WARNING

注意这个代码用来示范错误代码,业务里不要这样使用

arcgis json 转 GeoJSON

有时我们需要访问arcgis的数据服务,但是arcgis服务返回的是自己个json格式(esri json),不是 标准的GeoJSON格式,这时需要我们把arcgis json转成GeoJSON

参考仓库包:

terraformer

TIP

该包仅供参考,具体是否好用和我也不知道

TIP

高版本的arcgis server是直接支持输出GeoJSON格式的,可以在请求服务是直接请求GeoJSON格式

大量数据的空间计算

前端的空间计算推荐 jsts

  • turf不管是在性能和准确性都不好
  • 服务端常用的库也是JTS,不管是应用层还是数据库层都是JTS的派生库,可以做到API等一致性
  • JTS是经过大量的实践验证过的库

怎样解决大量数据的空间计算?

一般空间查询我们都是在服务端做的,例如使用(postgis),如果你的业务确实存在前端有大量的数据的情况下的空间计算, maptalks里提供了时间切片功能,我们可以利用他来解决前端大量数据的空间计算卡的问题

下面我们来演示个从1000w的点中查询点落在北京的点的集合

  • 使用jsts完成空间计算
  • 利用runTaskAsync来解决主线程卡的问题

构造1000w的点集合

因为1000w的点数据量真的太大了,无法使用静态的geojson数据,否则网络请求要好多时间,因而这里在简单的在内存里mock了数据

js
const minx = 73.66,
    maxx = 135.05,
    miny = 3.85,
    maxy = 53.55;

const TENK = 10000;
function randomFeatures(callback) {
    const dx = maxx - minx,
        dy = maxy - miny;
    const data = [];
    const total = 1000 * TENK;
    const pageSize = 10 * TENK;
    const count = Math.ceil(total / pageSize);
    const run = () => {
        for (let i = 0, len = pageSize; i < len; i++) {
            const x = Math.random() * dx + minx;
            const y = Math.random() * dy + miny;
            data.push({
                type: "Feature",
                geometry: {
                    coordinates: [x, y],
                    type: "Point",
                },
            });
        }
    };
    runTaskAsync({ count, run }).then(() => {
        callback(data);
    });
}

使用runTaskAsync运行jsts空间查询

js
function query() {
    showLoading("计算ing...");
    debugLayer.clear();
    const time = "randomFeatures";
    console.time(time);
    //生成测试数据
    randomFeatures((data) => {
        console.timeEnd(time);
        const time1 = "sp query";
        console.time(time1);
        const pageSize = 2 * TENK;
        const count = Math.ceil(data.length / pageSize);
        let page = 1;
        const run = () => {
            const start = (page - 1) * pageSize,
                end = page * pageSize;
            const list = data.slice(start, end);
            const geos = list.filter((feature) => {
                const geo = geoJSONRender.read(feature);
                return beijingGeo.geometry.intersects(geo.geometry);
            });
            page++;
            return geos;
        };
        //运行空间查询函数
        runTaskAsync({ count, run }).then((result) => {
            console.timeEnd(time1);
            const features = [];
            result.forEach((f) => {
                for (let i = 0, len = f.length; i < len; i++) {
                    features.push(f[i]);
                }
            });
            const points = maptalks.GeoJSON.toGeometry(features, (geo) => {
                geo.setSymbol({
                    markerType: "ellipse",
                    markerWidth: 6,
                    markerHeight: 6,
                });
            });
            console.log(points);
            debugLayer.addGeometry(points);
            hideLoading();
        });
    });
}

This document is generated by mdpress