@@ -3,10 +3,6 @@ import { RegionImageSource } from './RegionImageSource.js';
33import { ProjectionScheme } from '../utils/ProjectionScheme.js' ;
44import { 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