@jupytergis/base 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -85,6 +85,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
85
85
  'VectorLayer',
86
86
  'ShapefileLayer',
87
87
  'WebGlLayer',
88
+ 'VectorTileLayer',
88
89
  ].includes(selectedLayer.type);
89
90
  const isIdentifying = current.model.isIdentifying;
90
91
  if (isIdentifying && !canIdentify) {
@@ -98,7 +99,12 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
98
99
  if (!selectedLayer) {
99
100
  return false;
100
101
  }
101
- return ['VectorLayer', 'ShapefileLayer', 'WebGlLayer'].includes(selectedLayer.type);
102
+ return [
103
+ 'VectorLayer',
104
+ 'ShapefileLayer',
105
+ 'WebGlLayer',
106
+ 'VectorTileLayer',
107
+ ].includes(selectedLayer.type);
102
108
  }, execute: args => {
103
109
  const current = tracker.currentWidget;
104
110
  if (!current) {
@@ -1,6 +1,46 @@
1
1
  // import { GeoJSONFeature } from 'geojson';
2
2
  import { useEffect, useState } from 'react';
3
3
  import { loadFile } from "../../../tools";
4
+ async function getGeoJsonProperties({ source, model, }) {
5
+ var _a;
6
+ const result = {};
7
+ const data = await loadFile({
8
+ filepath: (_a = source.parameters) === null || _a === void 0 ? void 0 : _a.path,
9
+ type: 'GeoJSONSource',
10
+ model,
11
+ });
12
+ if (!data) {
13
+ throw new Error('Failed to read GeoJSON data');
14
+ }
15
+ data.features.forEach((feature) => {
16
+ if (feature.properties) {
17
+ for (const [key, value] of Object.entries(feature.properties)) {
18
+ if (!result[key]) {
19
+ result[key] = new Set();
20
+ }
21
+ result[key].add(value);
22
+ }
23
+ }
24
+ });
25
+ return result;
26
+ }
27
+ function getVectorTileProperties({ model, sourceId, }) {
28
+ const result = {};
29
+ const features = model.getFeaturesForCurrentTile({ sourceId });
30
+ features.forEach(feature => {
31
+ var _a;
32
+ const props = (_a = feature.getProperties) === null || _a === void 0 ? void 0 : _a.call(feature);
33
+ if (props) {
34
+ for (const [key, value] of Object.entries(props)) {
35
+ if (!result[key]) {
36
+ result[key] = new Set();
37
+ }
38
+ result[key].add(value);
39
+ }
40
+ }
41
+ });
42
+ return result;
43
+ }
4
44
  export const useGetProperties = ({ layerId, model, }) => {
5
45
  const [featureProperties, setFeatureProperties] = useState({});
6
46
  const [isLoading, setIsLoading] = useState(true);
@@ -16,30 +56,24 @@ export const useGetProperties = ({ layerId, model, }) => {
16
56
  if (!source) {
17
57
  throw new Error('Source not found');
18
58
  }
19
- const data = await loadFile({
20
- filepath: (_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path,
21
- type: 'GeoJSONSource',
22
- model: model,
23
- });
24
- if (!data) {
25
- throw new Error('Failed to read GeoJSON data');
59
+ const sourceType = source === null || source === void 0 ? void 0 : source.type;
60
+ let result = {};
61
+ if (sourceType === 'GeoJSONSource') {
62
+ result = await getGeoJsonProperties({ source, model });
63
+ }
64
+ else if (sourceType === 'VectorTileSource') {
65
+ const sourceId = (_b = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _b === void 0 ? void 0 : _b.source;
66
+ result = getVectorTileProperties({ model, sourceId });
67
+ }
68
+ else {
69
+ throw new Error(`Unsupported source type: ${sourceType}`);
26
70
  }
27
- const result = {};
28
- data.features.forEach((feature) => {
29
- if (feature.properties) {
30
- Object.entries(feature.properties).forEach(([key, value]) => {
31
- if (!(key in result)) {
32
- result[key] = new Set();
33
- }
34
- result[key].add(value);
35
- });
36
- }
37
- });
38
71
  setFeatureProperties(result);
39
- setIsLoading(false);
40
72
  }
41
73
  catch (err) {
42
74
  setError(err);
75
+ }
76
+ finally {
43
77
  setIsLoading(false);
44
78
  }
45
79
  };
@@ -15,19 +15,19 @@ const RENDER_TYPE_OPTIONS = {
15
15
  Canonical: {
16
16
  component: Canonical,
17
17
  attributeChecker: getColorCodeFeatureAttributes,
18
- supportedLayerTypes: ['VectorLayer', 'HeatmapLayer'],
18
+ supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'],
19
19
  isTabbed: false,
20
20
  },
21
21
  Graduated: {
22
22
  component: Graduated,
23
23
  attributeChecker: getNumericFeatureAttributes,
24
- supportedLayerTypes: ['VectorLayer', 'HeatmapLayer'],
24
+ supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'],
25
25
  isTabbed: true,
26
26
  },
27
27
  Categorized: {
28
28
  component: Categorized,
29
29
  attributeChecker: getNumericFeatureAttributes,
30
- supportedLayerTypes: ['VectorLayer', 'HeatmapLayer'],
30
+ supportedLayerTypes: ['VectorLayer', 'VectorTileLayer', 'HeatmapLayer'],
31
31
  isTabbed: true,
32
32
  },
33
33
  Heatmap: {
@@ -126,13 +126,15 @@ const Graduated = ({ model, state, okSignalPromise, cancel, layerId, symbologyTa
126
126
  });
127
127
  newStyle['fill-color'] = colorExpr;
128
128
  newStyle['circle-fill-color'] = colorExpr;
129
+ newStyle['stroke-color'] = colorExpr;
130
+ newStyle['circle-stroke-color'] = colorExpr;
129
131
  }
130
132
  else {
131
133
  newStyle['fill-color'] = undefined;
132
134
  newStyle['circle-fill-color'] = undefined;
135
+ newStyle['stroke-color'] = colorManualStyleRef.current.strokeColor;
136
+ newStyle['circle-stroke-color'] = colorManualStyleRef.current.strokeColor;
133
137
  }
134
- newStyle['stroke-color'] = colorManualStyleRef.current.strokeColor;
135
- newStyle['circle-stroke-color'] = colorManualStyleRef.current.strokeColor;
136
138
  newStyle['stroke-width'] = colorManualStyleRef.current.strokeWidth;
137
139
  newStyle['circle-stroke-width'] = colorManualStyleRef.current.strokeWidth;
138
140
  // Apply radius symbology
@@ -1,4 +1,5 @@
1
1
  import { IDict } from '@jupytergis/schema';
2
+ import { ISubmitEvent } from '@rjsf/core';
2
3
  import { PathBasedSourcePropertiesForm } from './pathbasedsource';
3
4
  import { ISourceFormProps } from './sourceform';
4
5
  /**
@@ -13,5 +14,6 @@ export declare class GeoJSONSourcePropertiesForm extends PathBasedSourceProperti
13
14
  *
14
15
  * @param path - the path to validate.
15
16
  */
16
- protected _validatePath(path: string): Promise<void>;
17
+ protected _validatePath(path: string | undefined): Promise<void>;
18
+ protected onFormSubmit(e: ISubmitEvent<any>): void;
17
19
  }
@@ -1,4 +1,5 @@
1
1
  import * as geojson from '@jupytergis/schema/src/schema/geojson.json';
2
+ import { showErrorMessage } from '@jupyterlab/apputils';
2
3
  import { Ajv } from 'ajv';
3
4
  import { loadFile } from "../../../tools";
4
5
  import { PathBasedSourcePropertiesForm } from './pathbasedsource';
@@ -17,6 +18,10 @@ export class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
17
18
  if ((data === null || data === void 0 ? void 0 : data.path) !== '') {
18
19
  this.removeFormEntry('data', data, schema, uiSchema);
19
20
  }
21
+ if (this.props.formContext === 'create') {
22
+ schema.properties.path.description =
23
+ 'The local path to a GeoJSON file. (If no path/url is provided, an empty GeoJSON is created.)';
24
+ }
20
25
  super.processSchema(data, schema, uiSchema);
21
26
  }
22
27
  /**
@@ -28,7 +33,7 @@ export class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
28
33
  var _a;
29
34
  const extraErrors = this.state.extraErrors;
30
35
  let error = '';
31
- let valid = false;
36
+ let valid = true;
32
37
  if (path) {
33
38
  try {
34
39
  const geoJSONData = await loadFile({
@@ -45,9 +50,6 @@ export class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
45
50
  error = `"${path}" is not a valid GeoJSON file: ${e}`;
46
51
  }
47
52
  }
48
- else {
49
- error = 'Path is required';
50
- }
51
53
  if (!valid) {
52
54
  extraErrors.path = {
53
55
  __errors: [error],
@@ -64,4 +66,18 @@ export class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
64
66
  this.props.formErrorSignal.emit(!valid);
65
67
  }
66
68
  }
69
+ onFormSubmit(e) {
70
+ var _a, _b, _c;
71
+ if (((_c = (_b = (_a = this.state.extraErrors) === null || _a === void 0 ? void 0 : _a.path) === null || _b === void 0 ? void 0 : _b.__errors) === null || _c === void 0 ? void 0 : _c.length) >= 1) {
72
+ showErrorMessage('Invalid file', this.state.extraErrors.path.__errors[0]);
73
+ return;
74
+ }
75
+ if (!e.formData.path) {
76
+ e.formData.data = {
77
+ type: 'FeatureCollection',
78
+ features: [],
79
+ };
80
+ }
81
+ super.onFormSubmit(e);
82
+ }
67
83
  }
@@ -3,7 +3,7 @@ import { showErrorMessage } from '@jupyterlab/apputils';
3
3
  import { CommandRegistry } from '@lumino/commands';
4
4
  import { UUID } from '@lumino/coreutils';
5
5
  import { ContextMenu } from '@lumino/widgets';
6
- import { Collection, Map as OlMap, View, getUid } from 'ol';
6
+ import { Collection, Map as OlMap, View, getUid, } from 'ol';
7
7
  import Feature from 'ol/Feature';
8
8
  import { FullScreen, ScaleLine } from 'ol/control';
9
9
  import { singleClick } from 'ol/events/condition';
@@ -14,10 +14,9 @@ import { Heatmap as HeatmapLayer, Image as ImageLayer, Layer, Vector as VectorLa
14
14
  import TileLayer from 'ol/layer/Tile';
15
15
  import { fromLonLat, get as getProjection, toLonLat, transformExtent, } from 'ol/proj';
16
16
  import { register } from 'ol/proj/proj4.js';
17
- import RenderFeature from 'ol/render/Feature';
18
- import { GeoTIFF as GeoTIFFSource, ImageTile as ImageTileSource, Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, } from 'ol/source';
17
+ import RenderFeature, { toGeometry } from 'ol/render/Feature';
18
+ import { GeoTIFF as GeoTIFFSource, ImageTile as ImageTileSource, Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, Tile as TileSource, } from 'ol/source';
19
19
  import Static from 'ol/source/ImageStatic';
20
- import TileSource from 'ol/source/Tile';
21
20
  import { Circle, Fill, Stroke, Style } from 'ol/style';
22
21
  //@ts-expect-error no types for ol-pmtiles
23
22
  import { PMTilesRasterSource, PMTilesVectorSource } from 'ol-pmtiles';
@@ -665,7 +664,8 @@ export class MainView extends React.Component {
665
664
  switch (source.type) {
666
665
  case 'RasterSource': {
667
666
  const sourceParameters = source.parameters;
668
- const pmTiles = sourceParameters.url.endsWith('.pmtiles');
667
+ const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
668
+ sourceParameters.url.endsWith('pmtiles.gz');
669
669
  const url = this.computeSourceUrl(source);
670
670
  if (!pmTiles) {
671
671
  newSource = new XYZSource({
@@ -698,7 +698,8 @@ export class MainView extends React.Component {
698
698
  }
699
699
  case 'VectorTileSource': {
700
700
  const sourceParameters = source.parameters;
701
- const pmTiles = sourceParameters.url.endsWith('.pmtiles');
701
+ const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
702
+ sourceParameters.url.endsWith('pmtiles.gz');
702
703
  const url = this.computeSourceUrl(source);
703
704
  if (!pmTiles) {
704
705
  newSource = new VectorTileSource({
@@ -717,6 +718,16 @@ export class MainView extends React.Component {
717
718
  url: url,
718
719
  });
719
720
  }
721
+ newSource.on('tileloadend', (event) => {
722
+ const tile = event.tile;
723
+ const features = tile.getFeatures();
724
+ if (features && features.length > 0) {
725
+ this._model.syncTileFeatures({
726
+ sourceId: id,
727
+ features,
728
+ });
729
+ }
730
+ });
720
731
  break;
721
732
  }
722
733
  case 'GeoJSONSource': {
@@ -1581,6 +1592,39 @@ export class MainView extends React.Component {
1581
1592
  const layerId = Object.keys(selectedLayer)[0];
1582
1593
  const jgisLayer = this._model.getLayer(layerId);
1583
1594
  switch (jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) {
1595
+ case 'VectorTileLayer': {
1596
+ const geometries = [];
1597
+ const features = [];
1598
+ this._Map.forEachFeatureAtPixel(e.pixel, (feature) => {
1599
+ let geom;
1600
+ if (feature instanceof RenderFeature) {
1601
+ geom = toGeometry(feature);
1602
+ }
1603
+ else if ('getGeometry' in feature) {
1604
+ geom = feature.getGeometry();
1605
+ }
1606
+ const props = feature.getProperties();
1607
+ if (geom) {
1608
+ geometries.push(geom);
1609
+ }
1610
+ features.push(Object.assign({}, props));
1611
+ return true;
1612
+ });
1613
+ if (features.length > 0) {
1614
+ this._model.syncIdentifiedFeatures(features, this._mainViewModel.id);
1615
+ }
1616
+ if (geometries.length > 0) {
1617
+ for (const geom of geometries) {
1618
+ this._model.highlightFeatureSignal.emit(geom);
1619
+ }
1620
+ }
1621
+ else {
1622
+ const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
1623
+ const point = new Point(coordinate);
1624
+ this._model.highlightFeatureSignal.emit(point);
1625
+ }
1626
+ break;
1627
+ }
1584
1628
  case 'WebGlLayer': {
1585
1629
  const layer = this.getLayer(layerId);
1586
1630
  const data = layer.getData(e.pixel);
@@ -1,12 +1,14 @@
1
+ import { PageConfig } from '@jupyterlab/coreutils';
1
2
  import * as React from 'react';
2
3
  import { LayersBodyComponent } from './components/layers';
3
4
  import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/components/Tabs';
4
5
  import StacPanel from '../stacBrowser/components/StacPanel';
5
6
  import FilterComponent from './components/filter-panel/Filter';
6
7
  export const LeftPanel = (props) => {
8
+ const hideStacPanel = PageConfig.getOption('HIDE_STAC_PANEL') === 'true';
7
9
  const tabInfo = [
8
10
  { name: 'layers', title: 'Layers' },
9
- { name: 'stac', title: 'Stac Browser' },
11
+ ...(hideStacPanel ? [] : [{ name: 'stac', title: 'Stac Browser' }]),
10
12
  { name: 'filters', title: 'Filters' },
11
13
  ];
12
14
  const [curTab, setCurTab] = React.useState(tabInfo[0].name);
@@ -24,8 +26,8 @@ export const LeftPanel = (props) => {
24
26
  })),
25
27
  React.createElement(TabsContent, { value: "layers", className: "jgis-panel-tab-content jp-gis-layerPanel" },
26
28
  React.createElement(LayersBodyComponent, { model: props.model, commands: props.commands, state: props.state })),
27
- React.createElement(TabsContent, { value: "stac" },
28
- React.createElement(StacPanel, { model: props.model })),
29
+ !hideStacPanel && (React.createElement(TabsContent, { value: "stac" },
30
+ React.createElement(StacPanel, { model: props.model }))),
29
31
  React.createElement(TabsContent, { value: "filters", className: "jgis-panel-tab-content" },
30
32
  React.createElement(FilterComponent, { model: props.model }),
31
33
  ","))));
@@ -1,13 +1,17 @@
1
+ import { PageConfig } from '@jupyterlab/coreutils';
1
2
  import * as React from 'react';
2
3
  import { AnnotationsPanel } from './annotationPanel';
3
4
  import { IdentifyPanelComponent } from './components/identify-panel/IdentifyPanel';
4
5
  import { ObjectPropertiesReact } from './objectproperties';
5
6
  import { PanelTabs, TabsContent, TabsList, TabsTrigger, } from '../shared/components/Tabs';
6
7
  export const RightPanel = props => {
8
+ const hideAnnotationPanel = PageConfig.getOption('HIDE_ANNOTATION_PANEL') === 'true';
7
9
  const [selectedObjectProperties, setSelectedObjectProperties] = React.useState(undefined);
8
10
  const tabInfo = [
9
11
  { name: 'objectProperties', title: 'Object Properties' },
10
- { name: 'annotations', title: 'Annotations' },
12
+ ...(hideAnnotationPanel
13
+ ? []
14
+ : [{ name: 'annotations', title: 'Annotations' }]),
11
15
  { name: 'identifyPanel', title: 'Identify Features' },
12
16
  ];
13
17
  const [curTab, setCurTab] = React.useState(tabInfo[0].name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupytergis/base",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "A JupyterLab extension for 3D modelling.",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -44,7 +44,7 @@
44
44
  "@jupyter/collaboration": "^3.1.0",
45
45
  "@jupyter/react-components": "^0.16.6",
46
46
  "@jupyter/ydoc": "^2.0.0 || ^3.0.0",
47
- "@jupytergis/schema": "^0.7.0",
47
+ "@jupytergis/schema": "^0.8.1",
48
48
  "@jupyterlab/application": "^4.3.0",
49
49
  "@jupyterlab/apputils": "^4.3.0",
50
50
  "@jupyterlab/completer": "^4.3.0",