Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d11d813

Browse files
【update】优化提升 leaflet fgbLayer ol fgb source 渲染加载速度 review by songym
1 parent 3918cde commit d11d813

File tree

6 files changed

+410
-23
lines changed

6 files changed

+410
-23
lines changed

‎src/leaflet/core/Base.js‎

Lines changed: 167 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,182 @@ L.Control.Attribution.include({
1313
});
1414
L.Map.include({
1515
/*
16-
* 获取精确的像素坐标.
17-
* 当需要绘制比较平滑的曲线的时候可调用此方法代替latLngToContainerPoint
18-
* @param latlng
19-
*/
16+
* 获取精确的像素坐标.
17+
* 当需要绘制比较平滑的曲线的时候可调用此方法代替latLngToContainerPoint
18+
* @param latlng
19+
*/
2020
latLngToAccurateContainerPoint: function (latlng) {
2121
var projectedPoint = this.project(L.latLng(latlng));
2222
var layerPoint = projectedPoint._subtract(this.getPixelOrigin());
2323
return L.point(layerPoint).add(this._getMapPanePos());
2424
}
2525
});
26+
27+
var projectionNames = ['EPSG4326', 'EPSG3857', 'EPSG3395', 'EPSG900913'];
28+
29+
projectionNames.forEach(function (projectionName) {
30+
L.Util.extend(L.CRS[projectionName], {
31+
curScale: null,
32+
preZoom: 0,
33+
latLngToPoint: function (latlng, zoom) {
34+
var projectedPoint = this.projection.project(latlng);
35+
var scale;
36+
if (this.curScale && this.prevZoom === zoom) {
37+
scale = this.curScale;
38+
} else {
39+
scale = this.scale(zoom);
40+
this.curScale = scale;
41+
this.prevZoom = zoom;
42+
}
43+
return this.transformation._transform(projectedPoint, scale);
44+
}
45+
});
46+
});
47+
48+
L.Polyline.include({
49+
dirtyBounds: false,
50+
getBounds: function () {
51+
if (this.dirtyBounds && this._latlngs) {
52+
this._bounds = new L.latLngBounds();
53+
for (var i = 0, len = this._latlngs.length; i < len; i++) {
54+
this._bounds.extend(this._latlngs[i]);
55+
}
56+
this.dirtyBounds = false;
57+
}
58+
return this._bounds;
59+
},
60+
addLatLng: function (latlng, latlngs) {
61+
latlngs = latlngs || this._defaultShape();
62+
latlng = toLatLng(latlng);
63+
latlngs.push(latlng);
64+
this.dirtyBounds = true;
65+
return this.redraw();
66+
},
67+
_setLatLngs: function (latlngs) {
68+
this._latlngs = this._convertLatLngs(latlngs);
69+
},
70+
_convertLatLngs: function (latlngs) {
71+
var result = [],
72+
flat = L.LineUtil.isFlat(latlngs);
73+
74+
for (var i = 0, len = latlngs.length; i < len; i++) {
75+
if (flat) {
76+
result[i] = toLatLng(latlngs[i]);
77+
this.dirtyBounds = true;
78+
} else {
79+
result[i] = this._convertLatLngs(latlngs[i]);
80+
}
81+
}
82+
return result;
83+
},
84+
_project: function () {
85+
var pxBounds = new L.Bounds();
86+
this._rings = [];
87+
this._projectLatlngs(this._latlngs, this._rings, pxBounds);
88+
if (pxBounds.isValid()) {
89+
this._rawPxBounds = pxBounds;
90+
this._updateBounds();
91+
}
92+
}
93+
});
94+
95+
L.GeoJSON.include({
96+
initialize: function (geojson, options) {
97+
L.Util.setOptions(this, options);
98+
this._layers = {};
99+
this.defaultGeometryOptions = {};
100+
if (geojson) {
101+
this.addData(geojson);
102+
}
103+
},
104+
addData: function (geojson) {
105+
var features = Array.isArray(geojson) ? geojson : geojson.features,
106+
i, len, feature;
107+
108+
if (features) {
109+
for (i = 0, len = features.length; i < len; i++) {
110+
feature = features[i];
111+
if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
112+
this.addData(feature);
113+
}
114+
}
115+
return this;
116+
}
117+
118+
var options = this.options;
119+
120+
if (options.filter && !options.filter(geojson)) { return this; }
121+
var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson;
122+
var layer = L.GeoJSON.geometryToLayer(geojson, options);
123+
if (!layer) {
124+
return this;
125+
}
126+
layer.feature = L.GeoJSON.asFeature(geojson);
127+
128+
layer.defaultOptions = layer.options;
129+
var defaultGeometryOptions = this.defaultGeometryOptions[geometry.type];
130+
if (defaultGeometryOptions) {
131+
layer.commonOptions = defaultGeometryOptions;
132+
} else {
133+
this.defaultGeometryOptions[geometry.type] = L.Util.extend({}, layer.defaultOptions);
134+
}
135+
this.resetStyle(layer);
136+
137+
if (options.onEachFeature) {
138+
options.onEachFeature(geojson, layer);
139+
}
140+
141+
return this.addLayer(layer);
142+
},
143+
resetStyle: function (layer) {
144+
if (layer === undefined) {
145+
return this.eachLayer(this.resetStyle, this);
146+
}
147+
if (layer.commonOptions) {
148+
layer.options = layer.commonOptions
149+
} else {
150+
layer.options = L.Util.extend({}, layer.defaultOptions);
151+
}
152+
this._setLayerStyle(layer, this.options.style);
153+
return this;
154+
}
155+
});
156+
26157
wrapToGeoJSON([L.Polyline, L.Polygon, L.Marker, L.CircleMarker, L.Circle, L.LayerGroup]);
27158

28159
function wrapToGeoJSON(objClassArray) {
29-
objClassArray.map((objClass) => {
30-
objClass.defaultFunction = objClass.prototype.toGeoJSON;
31-
objClass.include({
32-
toGeoJSON: function (precision) {
33-
return objClass.defaultFunction.call(this, precision || L.toGeoJSONPrecision || 15);
34-
}
35-
})
36-
return objClass;
160+
objClassArray.map((objClass) => {
161+
objClass.defaultFunction = objClass.prototype.toGeoJSON;
162+
objClass.include({
163+
toGeoJSON: function (precision) {
164+
return objClass.defaultFunction.call(this, precision || L.toGeoJSONPrecision || 15);
165+
}
37166
})
167+
return objClass;
168+
})
169+
}
38170

171+
function toLatLng(a, b, c) {
172+
if (a instanceof L.latLng) {
173+
return a;
174+
}
175+
if (Array.isArray(a) && typeof a[0] !== 'object') {
176+
if (a.length === 3) {
177+
return new L.latLng(a[0], a[1], a[2]);
178+
}
179+
if (a.length === 2) {
180+
return new L.latLng(a[0], a[1]);
181+
}
182+
return null;
183+
}
184+
if (a === undefined || a === null) {
185+
return a;
186+
}
187+
if (typeof a === 'object' && 'lat' in a) {
188+
return new L.latLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
189+
}
190+
if (b === undefined) {
191+
return null;
192+
}
193+
return new L.latLng(a, b, c);
39194
}

‎src/leaflet/overlay/FGBLayer.js‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import RBush from 'rbush';
77
import { getIntersection } from '@supermapgis/iclient-common/util/MapCalculateUtil';
88
import { FetchRequest } from '@supermapgis/iclient-common/util/FetchRequest';
99
import { deserialize } from 'flatgeobuf/lib/mjs/geojson';
10+
import throttle from 'lodash.throttle';
1011

1112
/**
1213
* @class FGBLayer
@@ -28,6 +29,7 @@ import { deserialize } from 'flatgeobuf/lib/mjs/geojson';
2829
* @param {boolean} [options.idField='SmID'] - 是否指定要素字段作为唯一 ID,当 strategy 为 bbox 时生效。
2930
* @param {function} [options.featureLoader] - 要素自定义方法。
3031
* @param {function} [options.onEachFeature] - 要素创建时调用。
32+
* @param {number} [options.renderInterval=500] - 渲染间隔时间。
3133
* @usage
3234
* ```
3335
* // 浏览器
@@ -59,6 +61,8 @@ export var FGBLayer = L.FeatureGroup.extend({
5961
this._checked = false;
6062
this.idField = this.options.idField || 'SmID';
6163
this._updateFeaturesFn = this._updateFeatures.bind(this);
64+
this.renderInterval = this.options.renderInterval !== undefined ? this.options.renderInterval : 500;
65+
this.options.noClip = true;
6266
L.Util.setOptions(this, options);
6367
},
6468
onAdd: function (map) {
@@ -117,6 +121,11 @@ export var FGBLayer = L.FeatureGroup.extend({
117121
this.previousLayer = this.curLayer;
118122
this.curLayer.addTo(this);
119123
}
124+
let featureList = [];
125+
const throttleFn = throttle(()=>{
126+
this.curLayer.addData(featureList);
127+
featureList = [];
128+
}, this.renderInterval, { trailing: true, leading: false })
120129
for await (let feature of fgb) {
121130
if (this.strategy === 'bbox') {
122131
let id = feature.properties[this.idField];
@@ -134,7 +143,8 @@ export var FGBLayer = L.FeatureGroup.extend({
134143
if (this.options.featureLoader && typeof this.options.featureLoader === 'function') {
135144
feature = this.options.featureLoader(feature);
136145
}
137-
this.curLayer.addData(feature);
146+
featureList.push(feature);
147+
throttleFn();
138148
}
139149
},
140150
async _getStream(url) {

‎src/openlayers/overlay/FGB.js‎

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { FetchRequest } from '@supermapgis/iclient-common/util/FetchRequest';
77
import { all, bbox } from 'ol/loadingstrategy';
88
import { getIntersection } from '@supermapgis/iclient-common/util/MapCalculateUtil';
99
import GeoJSON from 'ol/format/GeoJSON';
10+
import throttle from 'lodash.throttle';
11+
import { getUid } from 'ol/util';
12+
1013
/**
1114
* @class FGB
1215
* @browsernamespace ol.source
@@ -26,12 +29,50 @@ import GeoJSON from 'ol/format/GeoJSON';
2629
* @param {boolean} [opt_options.useSpatialIndex] - 是否启用要素空间索引。
2730
* @param {boolean} [opt_options.wrapX] - 是否平铺地图。
2831
* @param {boolean} [opt_options.idField='SmID'] - 要素属性中表示唯一标识的字段,当 strategy 为 ol.loadingstrategy.bbox 时生效。
32+
* @param {number} [opt_options.renderInterval=500] - 渲染间隔时间。
2933
* @extends {ol.source.Vector}
3034
* @usage
3135
*/
36+
37+
class FeaturesCollection {
38+
constructor() {
39+
this.features = [];
40+
}
41+
42+
clear() {
43+
this.features = [];
44+
}
45+
46+
push(feature) {
47+
this.features.push(feature);
48+
}
49+
50+
isEmpty() {
51+
return this.features.length === 0;
52+
}
53+
getArray() {
54+
return this.features;
55+
}
56+
57+
forEach(fn) {
58+
return this.features.forEach(fn);
59+
}
60+
61+
getLength() {
62+
return this.features.length;
63+
}
64+
65+
remove(feature) {
66+
const index = this.features.indexOf(feature);
67+
if (index !== -1) {
68+
this.features.splice(index, 1);
69+
}
70+
}
71+
}
72+
3273
export class FGB extends VectorSource {
3374
constructor(options) {
34-
const baseOptions = Object.assign({ strategy: bbox }, options);
75+
const baseOptions = Object.assign({ strategy: bbox,useSpatialIndex: false }, options);
3576
delete baseOptions.url;
3677
delete baseOptions.extent;
3778
delete baseOptions.idField;
@@ -42,7 +83,8 @@ export class FGB extends VectorSource {
4283
this.extent = this.options.extent;
4384
this._idField = this.options.idField || 'SmID';
4485
this._validatedId = false;
45-
this._checked = false;
86+
this._checked = false;
87+
this.renderInterval = this.options.renderInterval !== undefined ? this.options.renderInterval : 500;
4688
this.setLoader(async function (extent) {
4789
if (this.strategy === bbox) {
4890
if (!this._validatedId) {
@@ -75,6 +117,11 @@ export class FGB extends VectorSource {
75117
};
76118
}
77119
const fgb = deserialize(url, rect);
120+
let featureList = [];
121+
const throttleFn = throttle(() => {
122+
this.addFeatures(featureList);
123+
featureList = [];
124+
}, this.renderInterval, { trailing: true, leading: false });
78125
for await (let feature of fgb) {
79126
let id = feature.properties[this._idField];
80127
if (id && !this._validatedId) {
@@ -88,7 +135,8 @@ export class FGB extends VectorSource {
88135
if (this.options.featureLoader && typeof this.options.featureLoader === 'function') {
89136
feature = this.options.featureLoader(feature);
90137
}
91-
this.addFeature(feature);
138+
featureList.push(feature);
139+
throttleFn();
92140
}
93141
}
94142

@@ -97,4 +145,21 @@ export class FGB extends VectorSource {
97145
return response;
98146
});
99147
}
148+
149+
bindFeaturesCollection_() {
150+
this.featuresCollection_ = new FeaturesCollection();
151+
}
152+
153+
addFeaturesInternal(features) {
154+
for (let i = 0, length = features.length; i < length; i++) {
155+
const feature = features[i];
156+
const featureKey = getUid(feature);
157+
this.setupChangeEvents_(featureKey, feature);
158+
this.featuresCollection_.push(features[i]);
159+
const geometry = feature.getGeometry();
160+
if (!geometry) {
161+
this.nullGeometryFeatures_[featureKey] = feature;
162+
}
163+
}
164+
}
100165
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /