Skip to content

Commit 67a61df

Browse files
authored
GeoJSONOverlay: Improve performance for complex data sets (#1419)
* Make changes to improve performance of geojson redraw * Optimize the texture content determination * Small variable reorder * small fixes
1 parent 4a764fc commit 67a61df

1 file changed

Lines changed: 66 additions & 50 deletions

File tree

src/three/plugins/images/sources/GeoJSONImageSource.js

Lines changed: 66 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ import { RegionImageSource } from './RegionImageSource.js';
33
import { ProjectionScheme } from '../utils/ProjectionScheme.js';
44
import { WGS84_ELLIPSOID } from '3d-tiles-renderer/three';
55

6-
// TODO: Add support for limited bounds
7-
// TODO: Add support for padding of tiles to avoid clipping "wide" elements
8-
// TODO: Need to clip / fix geojson shapes across the 180 degree boundary
9-
// TODO: Add support for easy regeneration when colors / styles / geojson change
106
// TODO: Consider option to support world-space thickness definitions. Eg world-space point size or line thickness in meters.
117

128
// function for calculating the the change in arc length at a given cartographic point
@@ -50,8 +46,11 @@ export class GeoJSONImageSource extends RegionImageSource {
5046
this.strokeWidth = strokeWidth;
5147
this.fillStyle = fillStyle;
5248

53-
this.projection = new ProjectionScheme();
49+
this.features = null;
50+
this.featureBounds = new Map();
5451
this.contentBounds = null;
52+
53+
this.projection = new ProjectionScheme();
5554
this.fetchData = ( ...args ) => fetch( ...args );
5655

5756
}
@@ -68,29 +67,23 @@ export class GeoJSONImageSource extends RegionImageSource {
6867

6968
}
7069

71-
// Compute bounds from GeoJSON data
72-
// Falls back to full projection bounds if no geojson or unable to compute an extent
73-
if ( this.geojson ) {
74-
75-
const geoBounds = this._geoJSONBounds().map( v => v * MathUtils.DEG2RAD );
76-
this.contentBounds = geoBounds;
77-
78-
} else {
79-
80-
this.contentBounds = this.projection.getBounds();
81-
82-
}
70+
this._updateCache( true );
8371

8472
}
8573

8674
hasContent( minX, minY, maxX, maxY ) {
8775

88-
return this.geojson !== null;
76+
// TODO: only return true if there are features within the range
77+
// TODO: this won't get "dirtied" - no textures will be generated for those cases
78+
// where "false" has already been returned on redraw. How to fix? Return a "false"
79+
// target to fill in later if needed?
80+
81+
const boundsDeg = [ minX, minY, maxX, maxY ].map( v => v * Math.RAD2DEG );
82+
return this._boundsIntersectBounds( boundsDeg, this.contentBounds );
8983

9084
}
9185

9286
// main fetch per region -> returns CanvasTexture
93-
// TODO: must be async?
9487
async fetchItem( tokens, signal ) {
9588

9689
// create canvas
@@ -114,6 +107,7 @@ export class GeoJSONImageSource extends RegionImageSource {
114107

115108
redraw() {
116109

110+
this._updateCache( true );
117111
this.forEachItem( ( tex, args ) => {
118112

119113
this._drawToCanvas( tex.image, args );
@@ -123,10 +117,49 @@ export class GeoJSONImageSource extends RegionImageSource {
123117

124118
}
125119

120+
_updateCache( force = false ) {
121+
122+
const { geojson, featureBounds } = this;
123+
if ( ! geojson || ( this.features && ! force ) ) {
124+
125+
return;
126+
127+
}
128+
129+
featureBounds.clear();
130+
131+
let minLon = Infinity;
132+
let minLat = Infinity;
133+
let maxLon = - Infinity;
134+
let maxLat = - Infinity;
135+
136+
// extract the relevant features
137+
this.features = this._featuresFromGeoJSON( geojson );
138+
this.features.forEach( feature => {
139+
140+
// save the feature bounds
141+
const bounds = this._getFeatureBounds( feature );
142+
featureBounds.set( feature, bounds );
143+
144+
// expand full content bounds
145+
const [ fMinLon, fMinLat, fMaxLon, fMaxLat ] = bounds;
146+
minLon = Math.min( minLon, fMinLon );
147+
minLat = Math.min( minLat, fMinLat );
148+
maxLon = Math.max( maxLon, fMaxLon );
149+
maxLat = Math.max( maxLat, fMaxLat );
150+
151+
} );
152+
153+
this.contentBounds = [ minLon, minLat, maxLon, maxLat ];
154+
155+
}
156+
126157
_drawToCanvas( canvas, tokens ) {
127158

159+
this._updateCache();
160+
128161
const [ minX, minY, maxX, maxY ] = tokens;
129-
const { projection, resolution, geojson } = this;
162+
const { projection, resolution, features } = this;
130163

131164
canvas.width = resolution;
132165
canvas.height = resolution;
@@ -145,9 +178,10 @@ export class GeoJSONImageSource extends RegionImageSource {
145178

146179
// draw features
147180
const ctx = canvas.getContext( '2d' );
148-
const features = this._featuresFromGeoJSON( geojson );
149181
for ( let i = 0; i < features.length; i ++ ) {
150182

183+
// TODO: Add support for padding of tiles to avoid clipping "wide" elements that may extend beyond
184+
// edge of the bounds like stroke, point size.
151185
const feature = features[ i ];
152186
if ( this._featureIntersectsTile( feature, regionBoundsDeg ) ) {
153187

@@ -159,20 +193,26 @@ export class GeoJSONImageSource extends RegionImageSource {
159193

160194
}
161195

162-
// bbox quick test in projected units
196+
// bounding box quick test in projected units
163197
_featureIntersectsTile( feature, boundsDeg ) {
164198

165-
const featureBoundsDeg = this._getFeatureBounds( feature );
199+
const featureBoundsDeg = this.featureBounds.get( feature );
166200
if ( ! featureBoundsDeg ) {
167201

168202
return false;
169203

170204
}
171205

206+
return this._boundsIntersectBounds( featureBoundsDeg, boundsDeg );
207+
208+
}
209+
210+
_boundsIntersectBounds( bounds1, bounds2 ) {
211+
172212
// check for intersection between bounds
173-
const [ fminX, fminY, fmaxX, fmaxY ] = featureBoundsDeg;
174-
const [ minX, minY, maxX, maxY ] = boundsDeg;
175-
return ! ( fmaxX < minX || fminX > maxX || fmaxY < minY || fminY > maxY );
213+
const [ minX1, minY1, maxX1, maxY1 ] = bounds1;
214+
const [ minX2, minY2, maxX2, maxY2 ] = bounds2;
215+
return ! ( maxX1 < minX2 || minX1 > maxX2 || maxY1 < minY2 || minY1 > maxY2 );
176216

177217
}
178218

@@ -435,28 +475,4 @@ export class GeoJSONImageSource extends RegionImageSource {
435475

436476
}
437477

438-
// Compute geographic bounds in degrees from current geojson.
439-
_geoJSONBounds() {
440-
441-
// TODO: add support for padding the bounding boxes
442-
const features = this._featuresFromGeoJSON( this.geojson );
443-
let minLon = Infinity;
444-
let minLat = Infinity;
445-
let maxLon = - Infinity;
446-
let maxLat = - Infinity;
447-
448-
features.forEach( feature => {
449-
450-
const [ fMinLon, fMinLat, fMaxLon, fMaxLat ] = this._getFeatureBounds( feature );
451-
minLon = Math.min( minLon, fMinLon );
452-
minLat = Math.min( minLat, fMinLat );
453-
maxLon = Math.max( maxLon, fMaxLon );
454-
maxLat = Math.max( maxLat, fMaxLat );
455-
456-
} );
457-
458-
return [ minLon, minLat, maxLon, maxLat ];
459-
460-
}
461-
462478
}

0 commit comments

Comments
 (0)