From f81a1c8e8957edfe33fa6450d7aef975c1c5f7fe Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Fri, 1 Aug 2025 16:33:12 +0530 Subject: [PATCH 01/12] trying to get the attribute data of tiles --- packages/base/src/mainview/mainView.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index 8b0a1ece8..bfd5053c6 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -71,6 +71,9 @@ import { XYZ as XYZSource, } from 'ol/source'; import Static from 'ol/source/ImageStatic'; +import { TileSourceEvent } from 'ol/source/Tile'; +import VectorTile from "ol/source/VectorTile"; +// @ts-ignore import TileSource from 'ol/source/Tile'; import { Circle, Fill, Stroke, Style } from 'ol/style'; import { Rule } from 'ol/style/flat'; @@ -621,6 +624,23 @@ export class MainView extends React.Component { }); } + console.log('New VectorTileSource created:', newSource); + + newSource.on('tileloadend', (event: TileSourceEvent) => { + console.log('Tile loaded:', event.tile.getKey()); + + const tile = event.tile as unknown as VectorTile; + + // getFeatures() is only available on VectorTiles + const features = tile.getProperties().features as FeatureLike[]; + if (features && features.length > 0) { + console.log('Tile loaded with features:'); + features.forEach((feature: FeatureLike) => { + console.log('Feature properties:', feature.getProperties()); + }); + } + }); + break; } case 'GeoJSONSource': { From 23f2dd083344bc1d1551f2f89a9208c7cecbdb15 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Fri, 1 Aug 2025 16:59:03 +0530 Subject: [PATCH 02/12] Make feature reading work --------- Co-authored-by: Greg Mooney --- packages/base/src/mainview/mainView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index bfd5053c6..d7a90d79b 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -37,7 +37,7 @@ import { User } from '@jupyterlab/services'; import { CommandRegistry } from '@lumino/commands'; import { JSONValue, UUID } from '@lumino/coreutils'; import { ContextMenu } from '@lumino/widgets'; -import { Collection, MapBrowserEvent, Map as OlMap, View, getUid } from 'ol'; +import { Collection, MapBrowserEvent, Map as OlMap, VectorTile, View, getUid } from 'ol'; import Feature, { FeatureLike } from 'ol/Feature'; import { FullScreen, ScaleLine } from 'ol/control'; import { Coordinate } from 'ol/coordinate'; @@ -72,7 +72,6 @@ import { } from 'ol/source'; import Static from 'ol/source/ImageStatic'; import { TileSourceEvent } from 'ol/source/Tile'; -import VectorTile from "ol/source/VectorTile"; // @ts-ignore import TileSource from 'ol/source/Tile'; import { Circle, Fill, Stroke, Style } from 'ol/style'; @@ -629,10 +628,10 @@ export class MainView extends React.Component { newSource.on('tileloadend', (event: TileSourceEvent) => { console.log('Tile loaded:', event.tile.getKey()); - const tile = event.tile as unknown as VectorTile; + const tile = event.tile as VectorTile; // getFeatures() is only available on VectorTiles - const features = tile.getProperties().features as FeatureLike[]; + const features = tile.getFeatures() if (features && features.length > 0) { console.log('Tile loaded with features:'); features.forEach((feature: FeatureLike) => { @@ -641,6 +640,7 @@ export class MainView extends React.Component { } }); + break; } case 'GeoJSONSource': { From e5367e84bbb581612e44b36ec875c3b561c09f2a Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Mon, 4 Aug 2025 14:58:28 +0530 Subject: [PATCH 03/12] some logic to sync and get features --- .../symbology/hooks/useGetProperties.ts | 73 +++++++++++++------ .../vector_layer/VectorRendering.tsx | 13 +++- packages/base/src/mainview/mainView.tsx | 35 ++++----- packages/schema/src/interfaces.ts | 3 + packages/schema/src/model.ts | 20 +++++ 5 files changed, 102 insertions(+), 42 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index eb1228a85..b82a92318 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -27,9 +27,10 @@ export const useGetProperties = ({ const [error, setError] = useState(); const getProperties = async () => { - if (!layerId) { - return; - } + if (!layerId) {return;} + + setIsLoading(true); + setError(undefined); try { const layer = model.getLayer(layerId); @@ -39,33 +40,63 @@ export const useGetProperties = ({ throw new Error('Source not found'); } - const data = await loadFile({ - filepath: source.parameters?.path, - type: 'GeoJSONSource', - model: model, - }); - - if (!data) { - throw new Error('Failed to read GeoJSON data'); - } + const sourceType = source?.type; const result: Record> = {}; - data.features.forEach((feature: GeoJSONFeature1) => { - if (feature.properties) { - Object.entries(feature.properties).forEach(([key, value]) => { - if (!(key in result)) { - result[key] = new Set(); + if (sourceType === 'GeoJSONSource') { + const data = await loadFile({ + filepath: source.parameters?.path, + type: 'GeoJSONSource', + model: model, + }); + + if (!data) {throw new Error('Failed to read GeoJSON data');} + + data.features.forEach((feature: GeoJSONFeature1) => { + if (feature.properties) { + for (const [key, value] of Object.entries(feature.properties)) { + if (!result[key]) {result[key] = new Set();} + result[key].add(value); } - result[key].add(value); - }); + } + }); + } else if (sourceType === 'VectorTileSource') { + if (!layer?.parameters) { + return; + } + console.log('model instance in hook:', model); + console.log( + 'getFeaturesForLayer:', + model.getFeaturesForLayer(layer.parameters.source), + ); + if (typeof model.getFeaturesForLayer !== 'function') { + throw new Error('model.getFeaturesForLayer not available'); } - }); + console.log('igoingin'); + + const features = await model.getFeaturesForLayer( + layer.parameters.source, + ); + console.log('urhrfb', features); + + if (!features) {throw new Error('No features found in extent');} + + features.forEach(feature => { + const props = feature.getProperties?.(); + if (props) { + for (const [key, value] of Object.entries(props)) { + if (!result[key]) {result[key] = new Set();} + result[key].add(value); + } + } + }); + } setFeatureProperties(result); - setIsLoading(false); } catch (err) { setError(err as Error); + } finally { setIsLoading(false); } }; diff --git a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx index 666401ec2..f6b0e5f4b 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx @@ -36,25 +36,30 @@ type SelectableRenderTypes = { const RENDER_TYPE_OPTIONS: RenderTypeOptions = { 'Single Symbol': { component: SimpleSymbol, - supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'], + supportedLayerTypes: [ + 'VectorLayer', + 'VectorTileLayer', + 'VectorTileLayer', + 'HeatmapLayer', + ], isTabbed: true, }, Canonical: { component: Canonical, attributeChecker: getColorCodeFeatureAttributes, - supportedLayerTypes: ['VectorLayer', 'HeatmapLayer'], + supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'], isTabbed: false, }, Graduated: { component: Graduated, attributeChecker: getNumericFeatureAttributes, - supportedLayerTypes: ['VectorLayer', 'HeatmapLayer'], + supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'], isTabbed: true, }, Categorized: { component: Categorized, attributeChecker: getNumericFeatureAttributes, - supportedLayerTypes: ['VectorLayer', 'HeatmapLayer'], + supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'], isTabbed: true, }, Heatmap: { diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index d7a90d79b..5c1943007 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -37,7 +37,14 @@ import { User } from '@jupyterlab/services'; import { CommandRegistry } from '@lumino/commands'; import { JSONValue, UUID } from '@lumino/coreutils'; import { ContextMenu } from '@lumino/widgets'; -import { Collection, MapBrowserEvent, Map as OlMap, VectorTile, View, getUid } from 'ol'; +import { + Collection, + MapBrowserEvent, + Map as OlMap, + VectorTile, + View, + getUid, +} from 'ol'; import Feature, { FeatureLike } from 'ol/Feature'; import { FullScreen, ScaleLine } from 'ol/control'; import { Coordinate } from 'ol/coordinate'; @@ -623,23 +630,16 @@ export class MainView extends React.Component { }); } - console.log('New VectorTileSource created:', newSource); - - newSource.on('tileloadend', (event: TileSourceEvent) => { - console.log('Tile loaded:', event.tile.getKey()); - - const tile = event.tile as VectorTile; - - // getFeatures() is only available on VectorTiles - const features = tile.getFeatures() - if (features && features.length > 0) { - console.log('Tile loaded with features:'); - features.forEach((feature: FeatureLike) => { - console.log('Feature properties:', feature.getProperties()); - }); - } - }); + newSource.on('tileloadend', (event: TileSourceEvent) => { + const tile = event.tile as VectorTile; + const features = tile.getFeatures(); + console.log('Loaded features:', features); + if (features && features.length > 0) { + this._model.syncTileFeatures(id, features); + } + console.log('model instance in mainView:', this._model); + }); break; } @@ -2260,4 +2260,5 @@ export class MainView extends React.Component { private _originalFeatures: IDict[]> = {}; private _highlightLayer: VectorLayer; private _updateCenter: CallableFunction; + // private _tileFeatureCache: Map> = new Map(); } diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index d408c3843..a67a37826 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -13,6 +13,7 @@ import { Contents, User } from '@jupyterlab/services'; import { JSONObject } from '@lumino/coreutils'; import { ISignal, Signal } from '@lumino/signaling'; import { SplitPanel } from '@lumino/widgets'; +import { FeatureLike } from 'ol/Feature'; import { IJGISContent, @@ -185,6 +186,8 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel { filePath: string; pathChanged: ISignal; + getFeaturesForLayer: (id: string) => FeatureLike[]; + syncTileFeatures: (id: string, features: FeatureLike[]) => void; getSettings(): IJupyterGISSettings; getContent(): IJGISContent; diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index 27622a0d9..3174ede3a 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -6,6 +6,7 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { PartialJSONObject } from '@lumino/coreutils'; import { ISignal, Signal } from '@lumino/signaling'; import Ajv from 'ajv'; +import { FeatureLike } from 'ol/Feature'; import { IJGISContent, @@ -78,6 +79,24 @@ export class JupyterGISModel implements IJupyterGISModel { return this._settings; } + getFeaturesForLayer(id: string): FeatureLike[] { + console.log('model features', this._tileFeatureCache.get(id)); + + return Array.from(this._tileFeatureCache.get(id) ?? []); + } + + syncTileFeatures(id: string, features: FeatureLike[]): void { + let featureSet = this._tileFeatureCache.get(id); + if (!featureSet) { + featureSet = new Set(); + this._tileFeatureCache.set(id, featureSet); + } + + features.forEach(feature => { + featureSet.add(feature); + }); + } + private _onSharedModelChanged = (sender: any, changes: any): void => { if (changes && changes?.objectChange?.length) { this._contentChanged.emit(void 0); @@ -793,6 +812,7 @@ export class JupyterGISModel implements IJupyterGISModel { private _geolocation: JgisCoordinates; private _geolocationChanged = new Signal(this); + private _tileFeatureCache: Map> = new Map(); } export namespace JupyterGISModel { From f5af89a73ba273a47eb04b6f307076d0c9ef99bc Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Mon, 4 Aug 2025 17:42:58 +0530 Subject: [PATCH 04/12] lint --- .../symbology/hooks/useGetProperties.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index b82a92318..1165bdf38 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -27,7 +27,9 @@ export const useGetProperties = ({ const [error, setError] = useState(); const getProperties = async () => { - if (!layerId) {return;} + if (!layerId) { + return; + } setIsLoading(true); setError(undefined); @@ -51,12 +53,16 @@ export const useGetProperties = ({ model: model, }); - if (!data) {throw new Error('Failed to read GeoJSON data');} + if (!data) { + throw new Error('Failed to read GeoJSON data'); + } data.features.forEach((feature: GeoJSONFeature1) => { if (feature.properties) { for (const [key, value] of Object.entries(feature.properties)) { - if (!result[key]) {result[key] = new Set();} + if (!result[key]) { + result[key] = new Set(); + } result[key].add(value); } } @@ -80,13 +86,17 @@ export const useGetProperties = ({ ); console.log('urhrfb', features); - if (!features) {throw new Error('No features found in extent');} + if (!features) { + throw new Error('No features found in extent'); + } features.forEach(feature => { const props = feature.getProperties?.(); if (props) { for (const [key, value] of Object.entries(props)) { - if (!result[key]) {result[key] = new Set();} + if (!result[key]) { + result[key] = new Set(); + } result[key].add(value); } } From ac02a86bb010f4e4db7d9ba6e48396b244a94110 Mon Sep 17 00:00:00 2001 From: Arjun Verma Date: Tue, 5 Aug 2025 12:55:16 +0530 Subject: [PATCH 05/12] Update packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx --- .../base/src/dialogs/symbology/vector_layer/VectorRendering.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx index f6b0e5f4b..1462c666d 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx @@ -39,7 +39,6 @@ const RENDER_TYPE_OPTIONS: RenderTypeOptions = { supportedLayerTypes: [ 'VectorLayer', 'VectorTileLayer', - 'VectorTileLayer', 'HeatmapLayer', ], isTabbed: true, From 5f615db2c0c900f7d15a99cc00d46220959c5007 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Tue, 5 Aug 2025 13:27:22 +0530 Subject: [PATCH 06/12] not needed --- .../base/src/dialogs/symbology/hooks/useGetProperties.ts | 3 --- .../src/dialogs/symbology/vector_layer/VectorRendering.tsx | 6 +----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index 1165bdf38..61ad704f4 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -31,9 +31,6 @@ export const useGetProperties = ({ return; } - setIsLoading(true); - setError(undefined); - try { const layer = model.getLayer(layerId); const source = model.getSource(layer?.parameters?.source); diff --git a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx index 1462c666d..26206be37 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/VectorRendering.tsx @@ -36,11 +36,7 @@ type SelectableRenderTypes = { const RENDER_TYPE_OPTIONS: RenderTypeOptions = { 'Single Symbol': { component: SimpleSymbol, - supportedLayerTypes: [ - 'VectorLayer', - 'VectorTileLayer', - 'HeatmapLayer', - ], + supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'], isTabbed: true, }, Canonical: { From 6e9a3f35c5b815db1e50a232a3f5cf07be57b671 Mon Sep 17 00:00:00 2001 From: Arjun Verma Date: Tue, 5 Aug 2025 13:30:48 +0530 Subject: [PATCH 07/12] Cleanup --- .../base/src/dialogs/symbology/hooks/useGetProperties.ts | 7 ------- packages/base/src/mainview/mainView.tsx | 2 -- packages/schema/src/model.ts | 1 - 3 files changed, 10 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index 61ad704f4..a13ab5bee 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -68,20 +68,13 @@ export const useGetProperties = ({ if (!layer?.parameters) { return; } - console.log('model instance in hook:', model); - console.log( - 'getFeaturesForLayer:', - model.getFeaturesForLayer(layer.parameters.source), - ); if (typeof model.getFeaturesForLayer !== 'function') { throw new Error('model.getFeaturesForLayer not available'); } - console.log('igoingin'); const features = await model.getFeaturesForLayer( layer.parameters.source, ); - console.log('urhrfb', features); if (!features) { throw new Error('No features found in extent'); diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index 05901a196..54dc910ea 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -645,12 +645,10 @@ export class MainView extends React.Component { newSource.on('tileloadend', (event: TileSourceEvent) => { const tile = event.tile as VectorTile; const features = tile.getFeatures(); - console.log('Loaded features:', features); if (features && features.length > 0) { this._model.syncTileFeatures(id, features); } - console.log('model instance in mainView:', this._model); }); break; diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index 3174ede3a..1ce6b79b8 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -80,7 +80,6 @@ export class JupyterGISModel implements IJupyterGISModel { } getFeaturesForLayer(id: string): FeatureLike[] { - console.log('model features', this._tileFeatureCache.get(id)); return Array.from(this._tileFeatureCache.get(id) ?? []); } From f16ac9128c31acd1acf7529d11fcda49cf209644 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Tue, 5 Aug 2025 13:32:52 +0530 Subject: [PATCH 08/12] lint --- packages/schema/src/model.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index 1ce6b79b8..f484e206a 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -80,7 +80,6 @@ export class JupyterGISModel implements IJupyterGISModel { } getFeaturesForLayer(id: string): FeatureLike[] { - return Array.from(this._tileFeatureCache.get(id) ?? []); } From f3526a247cfa19534fb9f6bc7eaef35c370b1b08 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Tue, 5 Aug 2025 13:47:13 +0530 Subject: [PATCH 09/12] apply graduated symbology to strokes too --- .../src/dialogs/symbology/vector_layer/types/Graduated.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx index ec18970cc..c4f680da5 100644 --- a/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx +++ b/packages/base/src/dialogs/symbology/vector_layer/types/Graduated.tsx @@ -166,13 +166,15 @@ const Graduated: React.FC = ({ }); newStyle['fill-color'] = colorExpr; newStyle['circle-fill-color'] = colorExpr; + newStyle['stroke-color'] = colorExpr; + newStyle['circle-stroke-color'] = colorExpr; } else { newStyle['fill-color'] = undefined; newStyle['circle-fill-color'] = undefined; + newStyle['stroke-color'] = colorManualStyleRef.current.strokeColor; + newStyle['circle-stroke-color'] = colorManualStyleRef.current.strokeColor; } - newStyle['stroke-color'] = colorManualStyleRef.current.strokeColor; - newStyle['circle-stroke-color'] = colorManualStyleRef.current.strokeColor; newStyle['stroke-width'] = colorManualStyleRef.current.strokeWidth; newStyle['circle-stroke-width'] = colorManualStyleRef.current.strokeWidth; From f5a34b7adc666fd31a0bd72388a84eb70b4a1bbd Mon Sep 17 00:00:00 2001 From: Arjun Verma Date: Wed, 6 Aug 2025 14:05:38 +0530 Subject: [PATCH 10/12] Apply suggestions from code review Co-authored-by: Greg Mooney Co-authored-by: Matt Fisher <3608264+mfisher87@users.noreply.github.com> --- packages/base/src/dialogs/symbology/hooks/useGetProperties.ts | 4 ++-- packages/base/src/mainview/mainView.tsx | 3 +-- packages/schema/src/interfaces.ts | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index a13ab5bee..ca8731068 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -72,11 +72,11 @@ export const useGetProperties = ({ throw new Error('model.getFeaturesForLayer not available'); } - const features = await model.getFeaturesForLayer( + const features = model.getFeaturesForLayer( layer.parameters.source, ); - if (!features) { + if (feature.length === 0) { throw new Error('No features found in extent'); } diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index 54dc910ea..b47b58cdc 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -79,11 +79,10 @@ import { Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, + Tile as TileSource } from 'ol/source'; import Static from 'ol/source/ImageStatic'; import { TileSourceEvent } from 'ol/source/Tile'; -// @ts-ignore -import TileSource from 'ol/source/Tile'; import { Circle, Fill, Stroke, Style } from 'ol/style'; import { Rule } from 'ol/style/flat'; //@ts-expect-error no types for ol-pmtiles diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index a67a37826..a34233dc8 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -186,8 +186,8 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel { filePath: string; pathChanged: ISignal; - getFeaturesForLayer: (id: string) => FeatureLike[]; - syncTileFeatures: (id: string, features: FeatureLike[]) => void; + getFeaturesForLayer: ({sourceId: string}) => FeatureLike[]; + syncTileFeatures: ({sourceId: string, features: FeatureLike[]}) => void; getSettings(): IJupyterGISSettings; getContent(): IJGISContent; From 8b871a7d8f6a040b262e1346341795f10aba2352 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Wed, 6 Aug 2025 14:47:13 +0530 Subject: [PATCH 11/12] fix brokage --- .../symbology/hooks/useGetProperties.ts | 12 ++-------- packages/base/src/mainview/mainView.tsx | 4 ++-- packages/schema/src/interfaces.ts | 15 +++++++++++-- packages/schema/src/model.ts | 22 +++++++++++-------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index ca8731068..0cb7f42ee 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -68,17 +68,9 @@ export const useGetProperties = ({ if (!layer?.parameters) { return; } - if (typeof model.getFeaturesForLayer !== 'function') { - throw new Error('model.getFeaturesForLayer not available'); - } - - const features = model.getFeaturesForLayer( - layer.parameters.source, - ); + const sourceId = layer.parameters.source; - if (feature.length === 0) { - throw new Error('No features found in extent'); - } + const features = model.getFeaturesForCurrentTile({ sourceId }); features.forEach(feature => { const props = feature.getProperties?.(); diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index b47b58cdc..cfa42a40c 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -79,7 +79,7 @@ import { Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, - Tile as TileSource + Tile as TileSource, } from 'ol/source'; import Static from 'ol/source/ImageStatic'; import { TileSourceEvent } from 'ol/source/Tile'; @@ -646,7 +646,7 @@ export class MainView extends React.Component { const features = tile.getFeatures(); if (features && features.length > 0) { - this._model.syncTileFeatures(id, features); + this._model.syncTileFeatures({ sourceId: id, features }); } }); diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index a34233dc8..e1149c98e 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -186,8 +186,19 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel { filePath: string; pathChanged: ISignal; - getFeaturesForLayer: ({sourceId: string}) => FeatureLike[]; - syncTileFeatures: ({sourceId: string, features: FeatureLike[]}) => void; + + getFeaturesForCurrentTile: ({ + sourceId, + }: { + sourceId: string; + }) => FeatureLike[]; + syncTileFeatures: ({ + sourceId, + features, + }: { + sourceId: string; + features: FeatureLike[]; + }) => void; getSettings(): IJupyterGISSettings; getContent(): IJGISContent; diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index f484e206a..31e9135b1 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -79,20 +79,24 @@ export class JupyterGISModel implements IJupyterGISModel { return this._settings; } - getFeaturesForLayer(id: string): FeatureLike[] { - return Array.from(this._tileFeatureCache.get(id) ?? []); + getFeaturesForCurrentTile({ sourceId }: { sourceId: string }): FeatureLike[] { + return Array.from(this._tileFeatureCache.get(sourceId) ?? []); } - syncTileFeatures(id: string, features: FeatureLike[]): void { - let featureSet = this._tileFeatureCache.get(id); + syncTileFeatures({ + sourceId, + features, + }: { + sourceId: string; + features: FeatureLike[]; + }): void { + let featureSet = this._tileFeatureCache.get(sourceId); + if (!featureSet) { featureSet = new Set(); - this._tileFeatureCache.set(id, featureSet); + this._tileFeatureCache.set(sourceId, featureSet); } - - features.forEach(feature => { - featureSet.add(feature); - }); + features.forEach(feature => featureSet.add(feature)); } private _onSharedModelChanged = (sender: any, changes: any): void => { From 1ecb02cdb40591a377d72d8271c6cc776d29f646 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Wed, 6 Aug 2025 16:29:45 +0530 Subject: [PATCH 12/12] move getproperties logic into methods --- .../symbology/hooks/useGetProperties.ts | 104 +++++++++++------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts index 0cb7f42ee..bd752a5c7 100644 --- a/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts +++ b/packages/base/src/dialogs/symbology/hooks/useGetProperties.ts @@ -16,6 +16,64 @@ interface IUseGetPropertiesResult { error?: Error; } +async function getGeoJsonProperties({ + source, + model, +}: { + source: any; + model: IJupyterGISModel; +}): Promise>> { + const result: Record> = {}; + + const data = await loadFile({ + filepath: source.parameters?.path, + type: 'GeoJSONSource', + model, + }); + + if (!data) { + throw new Error('Failed to read GeoJSON data'); + } + + data.features.forEach((feature: GeoJSONFeature1) => { + if (feature.properties) { + for (const [key, value] of Object.entries(feature.properties)) { + if (!result[key]) { + result[key] = new Set(); + } + result[key].add(value); + } + } + }); + + return result; +} + +function getVectorTileProperties({ + model, + sourceId, +}: { + model: IJupyterGISModel; + sourceId: string; +}): Record> { + const result: Record> = {}; + const features = model.getFeaturesForCurrentTile({ sourceId }); + + features.forEach(feature => { + const props = feature.getProperties?.(); + if (props) { + for (const [key, value] of Object.entries(props)) { + if (!result[key]) { + result[key] = new Set(); + } + result[key].add(value); + } + } + }); + + return result; +} + export const useGetProperties = ({ layerId, model, @@ -40,49 +98,15 @@ export const useGetProperties = ({ } const sourceType = source?.type; - - const result: Record> = {}; + let result: Record> = {}; if (sourceType === 'GeoJSONSource') { - const data = await loadFile({ - filepath: source.parameters?.path, - type: 'GeoJSONSource', - model: model, - }); - - if (!data) { - throw new Error('Failed to read GeoJSON data'); - } - - data.features.forEach((feature: GeoJSONFeature1) => { - if (feature.properties) { - for (const [key, value] of Object.entries(feature.properties)) { - if (!result[key]) { - result[key] = new Set(); - } - result[key].add(value); - } - } - }); + result = await getGeoJsonProperties({ source, model }); } else if (sourceType === 'VectorTileSource') { - if (!layer?.parameters) { - return; - } - const sourceId = layer.parameters.source; - - const features = model.getFeaturesForCurrentTile({ sourceId }); - - features.forEach(feature => { - const props = feature.getProperties?.(); - if (props) { - for (const [key, value] of Object.entries(props)) { - if (!result[key]) { - result[key] = new Set(); - } - result[key].add(value); - } - } - }); + const sourceId = layer?.parameters?.source; + result = getVectorTileProperties({ model, sourceId }); + } else { + throw new Error(`Unsupported source type: ${sourceType}`); } setFeatureProperties(result);