From e3e3303ff2b19b22856b7ba3082a32e7d0a37355 Mon Sep 17 00:00:00 2001 From: hu de yi Date: Fri, 28 Nov 2025 14:33:35 +0800 Subject: [PATCH 1/7] feat:Optimize worker data transmission for VT feature geometry --- packages/vt/src/common/Util.js | 56 ++++++ .../src/layer/layer/GeojsonVectorTileLayer.ts | 2 +- .../layer/renderer/VectorTileLayerRenderer.js | 14 +- .../vt/src/worker/layer/BaseLayerWorker.js | 183 ++++++++++-------- 4 files changed, 176 insertions(+), 79 deletions(-) diff --git a/packages/vt/src/common/Util.js b/packages/vt/src/common/Util.js index ab22a39961..9b574f9376 100644 --- a/packages/vt/src/common/Util.js +++ b/packages/vt/src/common/Util.js @@ -201,3 +201,59 @@ export function wrap(n, min, max) { const w = ((n - min) % d + d) % d + min; return w; } + + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); +export function encodeJSON(json) { + try { + const str = JSON.stringify(json); + return encoder.encode(str); + } catch (error) { + console.error('encode JSON to Uint8Array error:', error); + } +} + +export function decodeJSON(uint8Array) { + try { + const str = decoder.decode(uint8Array); + return JSON.parse(str); + } catch (error) { + console.error('decode Uint8Array to JSON error:', error); + } +} + +export function wrapVTFeatureGeometryInfo(tileCacheImage, features) { + if (!tileCacheImage || !features || !Array.isArray(features)) { + return; + } + const image = tileCacheImage; + const featuresTypeArray = image.featuresTypeArray; + //解码features的 typearray + if (featuresTypeArray && featuresTypeArray instanceof Uint8Array) { + image.featuresFullJSON = decodeJSON(featuresTypeArray); + delete image.featuresTypeArray; + } + const featuresFullJSON = image.featuresFullJSON; + + const linkTo = (dataList) => { + dataList = dataList || []; + if (featuresFullJSON) { + for (let i = 0, len = dataList.length; i < len; i++) { + const feature = dataList[i].feature; + let featureId = feature; + const isObj = isObject(featureId); + if (isObj) { + featureId = featureId.id; + } + const featureJSON = featuresFullJSON[featureId]; + if (featureJSON && isObj) { + // feature.properties = featureJSON.properties; + //把geometry信息补起来 + feature.geometry = featureJSON.geometry; + } + } + } + } + linkTo(features); +} diff --git a/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts b/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts index 40ea90f840..bd3d7ee307 100644 --- a/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts +++ b/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts @@ -9,7 +9,7 @@ import VectorTileLayer, { VectorTileLayerOptionsType } from "./VectorTileLayer"; const options = { //feature data to return from worker //for geojson layer, only need to return id of features - features: "id", + features: true, tileBuffer: 64, extent: 8192, pyramidMode: 1, diff --git a/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js b/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js index af5f77e0bc..cab4ddcd7f 100644 --- a/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js +++ b/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js @@ -4,7 +4,7 @@ import WorkerConnection from './worker/WorkerConnection'; import { EMPTY_VECTOR_TILE } from '../core/Constant'; import DebugPainter from './utils/DebugPainter'; import TileStencilRenderer from './stencil/TileStencilRenderer'; -import { extend, pushIn, getCentiMeterScale, isNil, isFunction } from '../../common/Util'; +import { extend, pushIn, getCentiMeterScale, isNil, isFunction, wrapVTFeatureGeometryInfo } from '../../common/Util'; import { default as convertToPainterFeatures, oldPropsKey } from './utils/convert_to_painter_features'; import { isFunctionDefinition } from '@maptalks/function-type'; import { meterToPoint } from '../plugins/Util'; @@ -1689,6 +1689,14 @@ class VectorTileLayerRenderer extends CanvasCompatible(TileLayerRendererable(Lay hits.push(picked); } }); + hits.forEach(item => { + const { data } = item; + const tile = data.tile; + if (tile) { + const tileCacheItem = this.tileCache.get(tile.id) || {}; + wrapVTFeatureGeometryInfo(tileCacheItem.image, [data]) + } + }); return hits; } @@ -1697,6 +1705,8 @@ class VectorTileLayerRenderer extends CanvasCompatible(TileLayerRendererable(Lay return; } if (tile.image && !tile.image._empty) { + delete tile.image.featuresTypeArray; + delete tile.image.featuresFullJSON; const styleCounter = tile.image && tile.image.style; const plugins = this._getStylePlugins(styleCounter); if (plugins) { @@ -2408,6 +2418,7 @@ function findFeatures(image) { if (!image.cache) { return []; } + for (const p in image.cache) { const data = image.cache[p]; if (!data.geometry) { @@ -2422,6 +2433,7 @@ function findFeatures(image) { if (empty !== undefined) { geometry.properties.features.empty = empty; } + wrapVTFeatureGeometryInfo(image, features); return features; } } diff --git a/packages/vt/src/worker/layer/BaseLayerWorker.js b/packages/vt/src/worker/layer/BaseLayerWorker.js index f40b46fa8a..c85adb95d1 100644 --- a/packages/vt/src/worker/layer/BaseLayerWorker.js +++ b/packages/vt/src/worker/layer/BaseLayerWorker.js @@ -1,4 +1,4 @@ -import { isNil, extend, isString, isObject, isNumber, pushIn, isFnTypeSymbol } from '../../common/Util'; +import { isNil, extend, isString, isObject, isNumber, pushIn, isFnTypeSymbol, encodeJSON } from '../../common/Util'; import { buildWireframe, build3DExtrusion } from '../builder/'; import { createFilter } from '@maptalks/feature-filter'; import { KEY_IDX } from '../../common/Constant'; @@ -130,6 +130,34 @@ export default class BaseLayerWorker { if (props) { extend(data.data, props); } + + + const features = data.data.features; + if (features) { + const newFeatures = {}; + for (const index in features) { + const feature = features[index]; + newFeatures[feature.id] = feature; + } + //完整的feature json 进行编码 + const uint8Array = encodeJSON(newFeatures); + if (uint8Array) { + data.data.featuresTypeArray = uint8Array; + data.buffers = data.buffer || []; + data.buffers.push(uint8Array.buffer); + } + const onlyId = this.options.features === 'id'; + for (const index in features) { + const featureData = features[index]; + if (uint8Array) { + //删除geometry,已经包含在featuresTypeArray + delete featureData.geometry; + } + if (onlyId) { + features[index] = featureData.id; + } + } + } cb(null, data.data, data.buffers); }).catch(err => { cb(err); @@ -426,48 +454,49 @@ export default class BaseLayerWorker { } if (feature && (options.features || hasFnTypeProps && feaTags[i])) { //reset feature's marks - if (options.features === 'id') { - allFeas[i] = feature.id; - } else { - if (!options.pickingGeometry) { - delete feature.geometry; - } - delete feature.extent; - delete feature.properties['$layer']; - delete feature.properties['$type']; - // _getFeaturesToMerge 中用于排序的临时字段 - delete feature['__index']; - const originalFeature = feature.originalFeature; - if (originalFeature) { - const properties = feature.properties; - const fea = extend({}, feature.originalFeature); - // fea.properties = extend({}, feature.originalFeature.properties, properties); - delete properties[oldPropsKey]; - fea.customProps = extend({}, properties); - - feature = fea; - } - const o = extend({}, feature); - if (hasFnTypeProps && feaTags[i] && (!options.features || options.features === 'transient')) { - // 只输出symbol中用到的属性 - const pluginIndexs = feaTags[i]; - for (let j = 0; j < pluginIndexs.length; j++) { - const props = fnTypeProps[j]; - if (!props) { - continue; - } - props.forEach(p => { - const properties = originalFeature ? originalFeature.properties : feature.properties; - if (!properties[fntypePropsKey]) { - properties[fntypePropsKey] = new Set(); - } - properties[fntypePropsKey].add(p); - needClearNoneFnTypeProps = true; - }); + //always return full feature json body + // if (options.features === 'id') { + // allFeas[i] = feature.id; + // } else { + // if (!options.pickingGeometry) { + // delete feature.geometry; + // } + delete feature.extent; + delete feature.properties['$layer']; + delete feature.properties['$type']; + // _getFeaturesToMerge 中用于排序的临时字段 + delete feature['__index']; + const originalFeature = feature.originalFeature; + if (originalFeature) { + const properties = feature.properties; + const fea = extend({}, feature.originalFeature); + // fea.properties = extend({}, feature.originalFeature.properties, properties); + delete properties[oldPropsKey]; + fea.customProps = extend({}, properties); + + feature = fea; + } + const o = extend({}, feature); + if (hasFnTypeProps && feaTags[i] && (!options.features || options.features === 'transient')) { + // 只输出symbol中用到的属性 + const pluginIndexs = feaTags[i]; + for (let j = 0; j < pluginIndexs.length; j++) { + const props = fnTypeProps[j]; + if (!props) { + continue; } + props.forEach(p => { + const properties = originalFeature ? originalFeature.properties : feature.properties; + if (!properties[fntypePropsKey]) { + properties[fntypePropsKey] = new Set(); + } + properties[fntypePropsKey].add(p); + needClearNoneFnTypeProps = true; + }); } - allFeas[i] = o; } + allFeas[i] = o; + // } } } if (needClearNoneFnTypeProps) { @@ -811,30 +840,30 @@ export default class BaseLayerWorker { function getDefaultRenderPlugin(type) { switch (type) { - case 1: - return { - type: 'native-point', - dataConfig: { + case 1: + return { type: 'native-point', - only2D: true - } - }; - case 2: - return { - type: 'native-line', - dataConfig: { + dataConfig: { + type: 'native-point', + only2D: true + } + }; + case 2: + return { type: 'native-line', - only2D: true - } - }; - case 3: - return { - type: 'fill', - dataConfig: { + dataConfig: { + type: 'native-line', + only2D: true + } + }; + case 3: + return { type: 'fill', - only2D: true - } - }; + dataConfig: { + type: 'fill', + only2D: true + } + }; } return null; } @@ -842,20 +871,20 @@ function getDefaultRenderPlugin(type) { function getDefaultSymbol(type) { switch (type) { - case 1: - return { - markerFill: '#f00', - markerSize: 10 - }; - case 2: - return { - lineColor: '#fff' - }; - case 3: - return { - polygonFill: '#00f', - polygonOpacity: 0.4 - }; + case 1: + return { + markerFill: '#f00', + markerSize: 10 + }; + case 2: + return { + lineColor: '#fff' + }; + case 3: + return { + polygonFill: '#00f', + polygonOpacity: 0.4 + }; } return null; } @@ -984,7 +1013,7 @@ function cloneFeaAndAppendCustomTags(features, zoom, pluginConfig, customProps) } const proxyGetter0 = { - get (obj, prop) { + get(obj, prop) { return prop in obj ? obj[prop] : obj.originalFeature[prop]; }, has(obj, key) { @@ -993,7 +1022,7 @@ const proxyGetter0 = { }; const proxyGetter1 = { - get: function(obj, prop) { + get: function (obj, prop) { return prop in obj ? obj[prop] : obj[oldPropsKey][prop]; }, has(obj, key) { From 3182b7a1371519612623673409bcb738afc62dac Mon Sep 17 00:00:00 2001 From: hu de yi Date: Fri, 28 Nov 2025 16:34:59 +0800 Subject: [PATCH 2/7] updates --- packages/vt/src/common/Util.js | 14 ++++++-- .../src/layer/layer/GeojsonVectorTileLayer.ts | 2 +- .../vt/src/layer/layer/VectorTileLayer.ts | 32 +++++++++++-------- .../layer/renderer/VectorTileLayerRenderer.js | 10 +++--- .../utils/convert_to_painter_features.js | 11 ++++--- packages/vt/test/specs/picking.spec.js | 2 +- 6 files changed, 43 insertions(+), 28 deletions(-) diff --git a/packages/vt/src/common/Util.js b/packages/vt/src/common/Util.js index 9b574f9376..7572607154 100644 --- a/packages/vt/src/common/Util.js +++ b/packages/vt/src/common/Util.js @@ -223,7 +223,11 @@ export function decodeJSON(uint8Array) { } } -export function wrapVTFeatureGeometryInfo(tileCacheImage, features) { +export function wrapVTFeatureGeometryInfo(layerFeatueValue, tileCacheImage, features) { + // layer features is 0 or false + if (!layerFeatueValue) { + return; + } if (!tileCacheImage || !features || !Array.isArray(features)) { return; } @@ -250,7 +254,13 @@ export function wrapVTFeatureGeometryInfo(tileCacheImage, features) { if (featureJSON && isObj) { // feature.properties = featureJSON.properties; //把geometry信息补起来 - feature.geometry = featureJSON.geometry; + if (!feature.geometry || !feature.geometry.coordinates) { + feature.geometry = featureJSON.geometry; + feature.geometry.isMVTCoordinates = true; + if (!feature.geometry.type) { + feature.geometry.type = featureJSON.type; + } + } } } } diff --git a/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts b/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts index bd3d7ee307..40ea90f840 100644 --- a/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts +++ b/packages/vt/src/layer/layer/GeojsonVectorTileLayer.ts @@ -9,7 +9,7 @@ import VectorTileLayer, { VectorTileLayerOptionsType } from "./VectorTileLayer"; const options = { //feature data to return from worker //for geojson layer, only need to return id of features - features: true, + features: "id", tileBuffer: 64, extent: 8192, pyramidMode: 1, diff --git a/packages/vt/src/layer/layer/VectorTileLayer.ts b/packages/vt/src/layer/layer/VectorTileLayer.ts index 554d2cfdb0..a200541429 100644 --- a/packages/vt/src/layer/layer/VectorTileLayer.ts +++ b/packages/vt/src/layer/layer/VectorTileLayer.ts @@ -1642,13 +1642,15 @@ class VectorTileLayer extends maptalks.TileLayer { point.y * dpr, options ); - if ( - (this.options as any)["features"] && - (this.options as any)["features"] !== "id" - ) { - // 将瓦片坐标转成经纬度坐标 - results = this._convertPickedFeature(results); - } + // if ( + // (this.options as any)["features"] && + // (this.options as any)["features"] !== "id" + // ) { + // // 将瓦片坐标转成经纬度坐标 + // results = this._convertPickedFeature(results); + // } + //always _convertPickedFeature + results = this._convertPickedFeature(results); if (options && options.filter) { return results.filter(g => options.filter(g)); } else { @@ -1680,13 +1682,15 @@ class VectorTileLayer extends maptalks.TileLayer { const type = pick.data.feature.type; pick.data.feature.type = "Feature"; // pick.data.feature.type = getFeatureType(pick.data.feature); - pick.data.feature.geometry = this._convertGeometry( - type, - geometry, - nw, - extent, - res - ); + if (geometry.isMVTCoordinates) { + pick.data.feature.geometry = this._convertGeometry( + geometry.type || type, + geometry, + nw, + extent, + res + ); + } // pick.data.feature.geometry = this._convertGeometryCoords(geometry, nw, extent, res); } } diff --git a/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js b/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js index cab4ddcd7f..3dd49d1037 100644 --- a/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js +++ b/packages/vt/src/layer/renderer/VectorTileLayerRenderer.js @@ -649,7 +649,7 @@ class VectorTileLayerRenderer extends CanvasCompatible(TileLayerRendererable(Lay continue; } const { info, image } = cache; - const features = findFeatures(image); + const features = findFeatures(this.layer, image); renderedFeatures.push({ tile: { id: info.id, x: info.x, y: info.y, z: info.z, url: info.url }, current: !!this.tilesInView[info.id], @@ -1690,11 +1690,11 @@ class VectorTileLayerRenderer extends CanvasCompatible(TileLayerRendererable(Lay } }); hits.forEach(item => { - const { data } = item; + const data = item.data || {}; const tile = data.tile; if (tile) { const tileCacheItem = this.tileCache.get(tile.id) || {}; - wrapVTFeatureGeometryInfo(tileCacheItem.image, [data]) + wrapVTFeatureGeometryInfo(this.layer.options.features, tileCacheItem.image, [data]) } }); return hits; @@ -2414,7 +2414,7 @@ function getTileViewport(tileSize) { }; } -function findFeatures(image) { +function findFeatures(layer, image) { if (!image.cache) { return []; } @@ -2433,7 +2433,7 @@ function findFeatures(image) { if (empty !== undefined) { geometry.properties.features.empty = empty; } - wrapVTFeatureGeometryInfo(image, features); + wrapVTFeatureGeometryInfo(layer.options.features, image, features); return features; } } diff --git a/packages/vt/src/layer/renderer/utils/convert_to_painter_features.js b/packages/vt/src/layer/renderer/utils/convert_to_painter_features.js index d0aa35c191..243054cc28 100644 --- a/packages/vt/src/layer/renderer/utils/convert_to_painter_features.js +++ b/packages/vt/src/layer/renderer/utils/convert_to_painter_features.js @@ -1,6 +1,6 @@ import * as maptalks from 'maptalks'; import { KEY_IDX } from '../../../common/Constant'; -import { extend } from '../../../common/Util'; +import { extend, isObject } from '../../../common/Util'; const KEY_IDX_NAME = (KEY_IDX + '').trim(); @@ -13,7 +13,8 @@ export default function convertToPainterFeatures(features, feaIndexes, layerId, for (let ii = 0, ll = data.length; ii < ll; ii++) { let feature = feaIndexes ? features[feaIndexes[ii]] : features[ii]; if (layer.options['features'] === 'id' && layer.getFeature) { - feature = layer.getFeature(feature); + const featureId = isObject(feature) ? feature.id : feature; + feature = layer.getFeature(featureId); feature.layer = layerId; } if (layer instanceof maptalks.TileLayer) { @@ -46,11 +47,11 @@ export const oldPropsKey = '__original_properties'; export const externalPropsKey = '__external_properties'; const proxyGetter = { - get: function(obj, prop) { + get: function (obj, prop) { return prop in obj ? obj[prop] : (obj[oldPropsKey][prop] || obj[externalPropsKey] && obj[externalPropsKey][prop]); }, - has: function(obj, prop) { - return (prop in obj) || (prop in obj[oldPropsKey]) || obj[externalPropsKey] && (prop in obj[externalPropsKey]); + has: function (obj, prop) { + return (prop in obj) || (prop in obj[oldPropsKey]) || obj[externalPropsKey] && (prop in obj[externalPropsKey]); } }; diff --git a/packages/vt/test/specs/picking.spec.js b/packages/vt/test/specs/picking.spec.js index a0e95d5db8..3dc43cbddd 100644 --- a/packages/vt/test/specs/picking.spec.js +++ b/packages/vt/test/specs/picking.spec.js @@ -1639,7 +1639,7 @@ describe('picking specs', () => { if (count === 5) { const picked = layer.identifyAtPoint(new maptalks.Point(map.width / 2, map.height / 2)); assert(picked[0].data.feature.type === 'Feature'); - assert(picked[0].data.feature.geometry.type === 'Polygon'); + assert(picked[0].data.feature.geometry.type.includes('Polygon')); assert(Math.abs(picked[0].data.feature.geometry.coordinates[0][0][0]) < 10); assert(Math.abs(picked[0].data.feature.geometry.coordinates[0][0][1]) < 10); done(); From 130f0ac86234ad303c4071050037cbcaf8fa02c5 Mon Sep 17 00:00:00 2001 From: hu de yi Date: Fri, 28 Nov 2025 16:44:01 +0800 Subject: [PATCH 3/7] updates --- .../vt/src/layer/layer/VectorTileLayer.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/vt/src/layer/layer/VectorTileLayer.ts b/packages/vt/src/layer/layer/VectorTileLayer.ts index a200541429..46d11ca8db 100644 --- a/packages/vt/src/layer/layer/VectorTileLayer.ts +++ b/packages/vt/src/layer/layer/VectorTileLayer.ts @@ -13,7 +13,7 @@ import type { import Color from 'color'; import { getVectorPacker } from "../../packer/inject"; import { compress, uncompress } from "./Compress"; -import { extend, hasOwn, isNil, isObject, isString, pushIn } from "../../common/Util"; +import { extend, hasOwn, isNil, isNumber, isObject, isString, pushIn } from "../../common/Util"; import Ajax from "../../worker/util/Ajax"; import VectorTileLayerRenderer from "../renderer/VectorTileLayerRenderer"; @@ -670,16 +670,15 @@ class VectorTileLayer extends maptalks.TileLayer { if (!feature || !tile || !geometry) { continue; } - if (geometry.type) { - continue; - } - const { x, y, res, extent } = tile; - if (x !== tempX || y !== tempY || res !== tempRes) { - tempNW = tileConfig.getTilePointNW(x, y, res); + if (geometry.isMVTCoordinates) { + const { x, y, res, extent } = tile; + if (x !== tempX || y !== tempY || res !== tempRes) { + tempNW = tileConfig.getTilePointNW(x, y, res); + } + const type = feature.type; + const geo = this._convertGeometry(geometry.type || type, geometry, tempNW, extent, res); + feature.geometry = geo; } - const type = feature.type; - const geo = this._convertGeometry(type, geometry, tempNW, extent, res); - feature.geometry = geo; } } From 9e46d5111dbaad546a653a238a417d96301afab6 Mon Sep 17 00:00:00 2001 From: hu de yi Date: Fri, 28 Nov 2025 16:53:40 +0800 Subject: [PATCH 4/7] updates --- .../vt/src/layer/layer/VectorTileLayer.ts | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/vt/src/layer/layer/VectorTileLayer.ts b/packages/vt/src/layer/layer/VectorTileLayer.ts index 46d11ca8db..b55f5a217b 100644 --- a/packages/vt/src/layer/layer/VectorTileLayer.ts +++ b/packages/vt/src/layer/layer/VectorTileLayer.ts @@ -670,15 +670,13 @@ class VectorTileLayer extends maptalks.TileLayer { if (!feature || !tile || !geometry) { continue; } - if (geometry.isMVTCoordinates) { - const { x, y, res, extent } = tile; - if (x !== tempX || y !== tempY || res !== tempRes) { - tempNW = tileConfig.getTilePointNW(x, y, res); - } - const type = feature.type; - const geo = this._convertGeometry(geometry.type || type, geometry, tempNW, extent, res); - feature.geometry = geo; + const { x, y, res, extent } = tile; + if (x !== tempX || y !== tempY || res !== tempRes) { + tempNW = tileConfig.getTilePointNW(x, y, res); } + const type = feature.type; + const geo = this._convertGeometry(type, geometry, tempNW, extent, res); + feature.geometry = geo; } } @@ -1681,15 +1679,14 @@ class VectorTileLayer extends maptalks.TileLayer { const type = pick.data.feature.type; pick.data.feature.type = "Feature"; // pick.data.feature.type = getFeatureType(pick.data.feature); - if (geometry.isMVTCoordinates) { - pick.data.feature.geometry = this._convertGeometry( - geometry.type || type, - geometry, - nw, - extent, - res - ); - } + pick.data.feature.geometry = this._convertGeometry( + type, + geometry, + nw, + extent, + res + ); + // pick.data.feature.geometry = this._convertGeometryCoords(geometry, nw, extent, res); } } @@ -1704,10 +1701,17 @@ class VectorTileLayer extends maptalks.TileLayer { extent: number, res: number ) { + //not mvt coordinates + if (!geometry.isMVTCoordinates) { + return geometry; + } //the geometry has convert if (geometry.type && geometry.coordinates) { return geometry; } + if (!isNumber(type)) { + type = geometry.type; + } let geoType: string, coordinates: any; if (type === 1) { if (geometry.length <= 1) { From c99010eb031b8204b28e96862eda672950f1ebdc Mon Sep 17 00:00:00 2001 From: hu de yi Date: Fri, 28 Nov 2025 17:39:22 +0800 Subject: [PATCH 5/7] update spec --- packages/vt/test/specs/picking.spec.js | 44 +++++++++++++++----------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/vt/test/specs/picking.spec.js b/packages/vt/test/specs/picking.spec.js index 3dc43cbddd..63be56ff3a 100644 --- a/packages/vt/test/specs/picking.spec.js +++ b/packages/vt/test/specs/picking.spec.js @@ -706,9 +706,9 @@ describe('picking specs', () => { customProperties: [ { "filter": true, - "properties": { + "properties": { "custom_prop_line_batch_id": "admin-0-boundary-bg" - } + } } ], symbol: { @@ -906,7 +906,7 @@ describe('picking specs', () => { - it('should return feature properties used in symbol', done => { + it('should return feature properties used in symbol', done => { const options = { // 不返回features features: 0, @@ -1116,7 +1116,7 @@ describe('picking specs', () => { const layer = new GeoJSONVectorTileLayer('gvt', options); layer.once('canvasisdirty', () => { const hit = layer.identify([13.41720, 52.52952])[0]; - const expectedFeature = { "type": "Feature", "geometry": { "type": "Polygon","coordinates": [[[13.417135053741617,52.52956625878565],[13.417226248848124,52.52956625878565],[13.417226248848124,52.52946625878565],[13.417135053741617,52.52946625878565],[13.417135053741617,52.52956625878565]]] },"properties": { "type": 1, "color": "#f00", "foo": "bar", "foo1": "bar1" },"id": 0,"layer": 0 }; + const expectedFeature = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[[13.417135053741617, 52.52956625878565], [13.417226248848124, 52.52956625878565], [13.417226248848124, 52.52946625878565], [13.417135053741617, 52.52946625878565], [13.417135053741617, 52.52956625878565]]] }, "properties": { "type": 1, "color": "#f00", "foo": "bar", "foo1": "bar1" }, "id": 0, "layer": 0 }; assert.deepEqual(hit.coordinate, [13.417199426755861, 52.52951893867223, -0.000006980199889114576]); assert.deepEqual(expectedFeature, hit.data.feature); done(); @@ -1253,15 +1253,15 @@ describe('picking specs', () => { map = new maptalks.Map(container, options.view || DEFAULT_VIEW); const layer = new PointLayer('gvt', [marker]); new GroupGLLayer('group', [layer], { - sceneConfig: { - environment: { - enable: true, - mode: 1, - level: 0, - brightness: 0, - }, - }, - }).addTo(map); + sceneConfig: { + environment: { + enable: true, + mode: 1, + level: 0, + brightness: 0, + }, + }, + }).addTo(map); layer.once('canvasisdirty', () => { const redPoint = layer.identify([0, 0]); assert(redPoint[0].geometry instanceof maptalks.Marker); @@ -1638,10 +1638,18 @@ describe('picking specs', () => { count++; if (count === 5) { const picked = layer.identifyAtPoint(new maptalks.Point(map.width / 2, map.height / 2)); - assert(picked[0].data.feature.type === 'Feature'); - assert(picked[0].data.feature.geometry.type.includes('Polygon')); - assert(Math.abs(picked[0].data.feature.geometry.coordinates[0][0][0]) < 10); - assert(Math.abs(picked[0].data.feature.geometry.coordinates[0][0][1]) < 10); + const feature = picked[0].data.feature; + assert(feature.type === 'Feature'); + let coordinates = feature.geometry.coordinates; + const type = feature.geometry.type; + assert(type.includes('Polygon')); + if (type === 'MultiPolygon') { + coordinates = coordinates[0][0][0]; + } else { + coordinates = coordinates[0][0]; + } + assert(Math.abs(coordinates[0]) < 10); + assert(Math.abs(coordinates[1]) < 10); done(); } }); @@ -1650,7 +1658,7 @@ describe('picking specs', () => { it('ciskip should enable stencil in VectorTileLayer FillPainter pick, maptalks/issues#832', done => { map = new maptalks.Map(container, { - center: [121.52861644,31.23331691], + center: [121.52861644, 31.23331691], zoom: 19, devicePixelRatio: 1 }); From 41dcd512198573f5ac0312e27c49d2f1a16fc01b Mon Sep 17 00:00:00 2001 From: deyihu Date: Sun, 30 Nov 2025 09:18:05 +0800 Subject: [PATCH 6/7] updates --- packages/vt/src/common/Util.js | 45 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/vt/src/common/Util.js b/packages/vt/src/common/Util.js index 7572607154..450c9d8245 100644 --- a/packages/vt/src/common/Util.js +++ b/packages/vt/src/common/Util.js @@ -223,12 +223,12 @@ export function decodeJSON(uint8Array) { } } -export function wrapVTFeatureGeometryInfo(layerFeatueValue, tileCacheImage, features) { +export function wrapVTFeatureGeometryInfo(layerOptionsFeatures, tileCacheImage, dataList) { // layer features is 0 or false - if (!layerFeatueValue) { + if (!layerOptionsFeatures) { return; } - if (!tileCacheImage || !features || !Array.isArray(features)) { + if (!tileCacheImage || !dataList || !Array.isArray(dataList)) { return; } const image = tileCacheImage; @@ -239,31 +239,28 @@ export function wrapVTFeatureGeometryInfo(layerFeatueValue, tileCacheImage, feat delete image.featuresTypeArray; } const featuresFullJSON = image.featuresFullJSON; - - const linkTo = (dataList) => { + if (featuresFullJSON) { dataList = dataList || []; - if (featuresFullJSON) { - for (let i = 0, len = dataList.length; i < len; i++) { - const feature = dataList[i].feature; - let featureId = feature; - const isObj = isObject(featureId); - if (isObj) { - featureId = featureId.id; - } - const featureJSON = featuresFullJSON[featureId]; - if (featureJSON && isObj) { - // feature.properties = featureJSON.properties; - //把geometry信息补起来 - if (!feature.geometry || !feature.geometry.coordinates) { - feature.geometry = featureJSON.geometry; - feature.geometry.isMVTCoordinates = true; - if (!feature.geometry.type) { - feature.geometry.type = featureJSON.type; - } + for (let i = 0, len = dataList.length; i < len; i++) { + const feature = dataList[i].feature; + let featureId = feature; + const isObj = isObject(featureId); + if (isObj) { + featureId = featureId.id; + } + const featureJSON = featuresFullJSON[featureId]; + if (featureJSON && isObj) { + // feature.properties = featureJSON.properties; + //把geometry信息补起来 + if (!feature.geometry || !feature.geometry.coordinates) { + feature.geometry = featureJSON.geometry; + feature.geometry.isMVTCoordinates = true; + if (!feature.geometry.type) { + feature.geometry.type = featureJSON.type; } } } } } - linkTo(features); + } From 2d0ba5cd56bb0fcd8d43fc182a0165dcfa06ab86 Mon Sep 17 00:00:00 2001 From: deyihu Date: Sun, 30 Nov 2025 09:57:49 +0800 Subject: [PATCH 7/7] update spec --- packages/vt/test/specs/update.fn-type.spec.js | 100 ++++++++++-------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/packages/vt/test/specs/update.fn-type.spec.js b/packages/vt/test/specs/update.fn-type.spec.js index 3f3b95d0e7..ecaffbb6ea 100644 --- a/packages/vt/test/specs/update.fn-type.spec.js +++ b/packages/vt/test/specs/update.fn-type.spec.js @@ -84,14 +84,16 @@ describe('update function type style specs', () => { }); it('property function type to property function type', done => { - const symbol = { lineColor: { - type: 'categorical', - property: 'type', - stops: [ - [1, '#f00'], - [2, '#f00'], - ] - }, lineWidth: 8, lineOpacity: 1 }; + const symbol = { + lineColor: { + type: 'categorical', + property: 'type', + stops: [ + [1, '#f00'], + [2, '#f00'], + ] + }, lineWidth: 8, lineOpacity: 1 + }; assertChangeStyle(done, symbol, [255, 0, 0, 255], [0, 255, 0, 255], layer => { layer.updateSymbol(0, { lineColor: { @@ -129,20 +131,22 @@ describe('update function type style specs', () => { }); it('property-zoom based to normal color', done => { - const symbol = { lineColor: { - type: 'categorical', - property: 'type', - stops: [ - [1, { - type: 'interval', - stops: [ - [map.getZoom(), '#00f'], - [map.getZoom() + 2, '#f00'], - ] - }], - [2, '#f00'], - ] - }, lineWidth: 8, lineOpacity: 1 }; + const symbol = { + lineColor: { + type: 'categorical', + property: 'type', + stops: [ + [1, { + type: 'interval', + stops: [ + [map.getZoom(), '#00f'], + [map.getZoom() + 2, '#f00'], + ] + }], + [2, '#f00'], + ] + }, lineWidth: 8, lineOpacity: 1 + }; assertChangeStyle(done, symbol, [0, 0, 255, 255], [255, 0, 0, 255], layer => { layer.updateSymbol(0, { lineColor: '#f00' @@ -152,20 +156,22 @@ describe('update function type style specs', () => { //TODO 目前暂时不支持zoom-property形式的function type it.skip('zoom-property-zoom based to normal color', done => { - const symbol = { lineColor: { - type: 'interval', - stops: [ - [map.getZoom(), { - type: 'categorical', - property: 'type', - stops: [ - [1, '#00f'], - [2, '#f00'], - ] - }], - [map.getZoom() + 2, '#f00'], - ] - }, lineWidth: 8, lineOpacity: 1 }; + const symbol = { + lineColor: { + type: 'interval', + stops: [ + [map.getZoom(), { + type: 'categorical', + property: 'type', + stops: [ + [1, '#00f'], + [2, '#f00'], + ] + }], + [map.getZoom() + 2, '#f00'], + ] + }, lineWidth: 8, lineOpacity: 1 + }; assertChangeStyle(done, symbol, [0, 0, 255, 255], [255, 0, 0, 255], layer => { layer.updateSymbol(0, { lineColor: '#f00' @@ -174,13 +180,15 @@ describe('update function type style specs', () => { }); it('zoom based to normal color', done => { - const symbol = { lineColor: { - type: 'interval', - stops: [ - [map.getZoom(), '#00f'], - [map.getZoom() + 2, '#f00'], - ] - }, lineWidth: 8, lineOpacity: 1 }; + const symbol = { + lineColor: { + type: 'interval', + stops: [ + [map.getZoom(), '#00f'], + [map.getZoom() + 2, '#f00'], + ] + }, lineWidth: 8, lineOpacity: 1 + }; assertChangeStyle(done, symbol, [0, 0, 255, 255], [255, 0, 0, 255], layer => { layer.updateSymbol(0, { lineColor: '#f00' @@ -214,6 +222,7 @@ describe('update function type style specs', () => { layer.once('canvasisdirty', () => { dirty = true; }); + let doned = false; //因为是setStyle时,数据会被清空重绘,所以需要监听两次canvasisdirty layer.on(isSetStyle ? 'canvasisdirty' : 'canvasisdirty', () => { if (!dirty) { @@ -229,7 +238,10 @@ describe('update function type style specs', () => { const pixel = readPixel(layer.getRenderer().canvas, x / 2, y / 2); //变成绿色 assert.deepEqual(pixel, expectedColor); - done(); + if (!doned) { + done(); + doned = true; + } } }); layer.addTo(map);