diff --git a/examples/buildings.jGIS b/examples/buildings.jGIS index b7e493a07..cc334546d 100644 --- a/examples/buildings.jGIS +++ b/examples/buildings.jGIS @@ -8,12 +8,12 @@ "name": "Buildings Vector Tile", "parameters": { "color": "#086600", - "opacity": 1.0, "source": "7a7ee6fd-c1e2-4c5d-a4e2-a7974db138a4", "type": "fill" }, "type": "VectorTileLayer", - "visible": true + "visible": true, + "opacity": 1 }, "f99eb7b0-5e38-4078-b310-36a0746472aa": { "name": "OpenStreetMap.Mapnik Layer", diff --git a/examples/earthquakes.jGIS b/examples/earthquakes.jGIS index e8a0e55ef..66dc265eb 100644 --- a/examples/earthquakes.jGIS +++ b/examples/earthquakes.jGIS @@ -10,6 +10,7 @@ "logicalOp": "all" }, "name": "Earthquakes", + "opacity": 1.0, "parameters": { "color": { "circle-fill-color": [ @@ -166,7 +167,6 @@ "stroke-color": "#62a0ea", "stroke-width": 1.25 }, - "opacity": 1.0, "source": "348d85fa-3a71-447f-8a64-e283ec47cc7c", "symbologyState": { "colorRamp": "cool", @@ -183,6 +183,7 @@ }, "8de7c2c0-6024-4716-b542-031a89fb87f9": { "name": "OpenStreetMap.Mapnik Layer", + "opacity": 1.0, "parameters": { "source": "b2ea427a-a51b-43ad-ae72-02cd900736d5" }, @@ -218,6 +219,7 @@ "name": "OpenStreetMap.Mapnik", "parameters": { "attribution": "(C) OpenStreetMap contributors", + "interpolate": false, "maxZoom": 19.0, "minZoom": 0.0, "provider": "OpenStreetMap", diff --git a/examples/france_hiking.jGIS b/examples/france_hiking.jGIS index a50d64bf0..b4362f55c 100644 --- a/examples/france_hiking.jGIS +++ b/examples/france_hiking.jGIS @@ -8,11 +8,11 @@ "0bfee293-9e2f-4434-8c5a-c90d19836bab": { "name": "WaymarkedTrails.hiking Layer", "parameters": { - "opacity": 0.6, "source": "82691e55-f9e2-43be-8a07-3ae0409af7b4" }, "type": "RasterLayer", - "visible": true + "visible": true, + "opacity": 0.6 }, "4a0703b3-ed56-4158-8a2e-e008c3d0fee2": { "name": "OpenStreetMap.Mapnik Layer", @@ -25,11 +25,11 @@ "7db81237-a579-4daa-938f-5e61fdfb17e7": { "name": "NASAGIBS.ModisTerraTrueColorCR Layer", "parameters": { - "opacity": 0.3, "source": "52252f5d-3cb7-45a8-a724-5793bf9950ec" }, "type": "RasterLayer", - "visible": true + "visible": true, + "opacity": 0.3 } }, "metadata": {}, diff --git a/examples/geoparquet.jgis b/examples/geoparquet.jgis index a634e73f8..26893e5dc 100644 --- a/examples/geoparquet.jgis +++ b/examples/geoparquet.jgis @@ -6,6 +6,7 @@ "layers": { "9556ca29-a5ec-41af-bf14-4b543c52aafe": { "name": "OpenStreetMap.Mapnik Layer", + "opacity": 0.8, "parameters": { "source": "2a52082b-7992-40dc-92d6-75e309a1ed27" }, @@ -14,8 +15,8 @@ }, "d7a2ad84-0750-4e9b-82c0-542fcc6b3265": { "name": "GeoParquet Vector Layer", + "opacity": 1.0, "parameters": { - "opacity": 1.0, "source": "c1da95b9-8a71-4fee-b4e3-6f0b5f53d2d4" }, "type": "VectorLayer", @@ -31,8 +32,8 @@ 20037508.342789244, 18740664.52872547 ], - "latitude": 0.0, - "longitude": 0.0, + "latitude": -7.105427357601002e-14, + "longitude": -1.6732426064452555e-14, "projection": "EPSG:3857", "zoom": 1.6220518194563762 }, @@ -42,6 +43,7 @@ "name": "OpenStreetMap.Mapnik", "parameters": { "attribution": "(C) OpenStreetMap contributors", + "interpolate": false, "maxZoom": 19.0, "minZoom": 0.0, "provider": "OpenStreetMap", diff --git a/examples/geotiff.jGIS b/examples/geotiff.jGIS index 83da48da8..5f9ca651f 100644 --- a/examples/geotiff.jGIS +++ b/examples/geotiff.jGIS @@ -137,7 +137,6 @@ 1.0 ] ], - "opacity": 1.0, "source": "8b1d4258-5d46-48da-b466-496d376b593d", "symbologyState": { "band": 1.0, @@ -149,7 +148,8 @@ } }, "type": "WebGlLayer", - "visible": true + "visible": true, + "opacity": 1 } }, "metadata": {}, diff --git a/examples/image.jGIS b/examples/image.jGIS index 0582c5951..b9500bace 100644 --- a/examples/image.jGIS +++ b/examples/image.jGIS @@ -6,8 +6,8 @@ "layers": { "756cb737-f817-4ba6-b7c0-8da0b97b9778": { "name": "Custom Image Layer Layer", + "opacity": 1.0, "parameters": { - "opacity": 1.0, "source": "fb9729b8-82c6-48ac-a12b-6343c0e037ae" }, "type": "ImageLayer", @@ -15,6 +15,7 @@ }, "acb76fb7-df6d-41da-8e08-0208a1c82136": { "name": "OpenStreetMap.Mapnik Layer", + "opacity": 0.4, "parameters": { "source": "5f7a1edf-1b76-4f82-893f-540d28998b1d" }, @@ -43,6 +44,7 @@ "name": "OpenStreetMap.Mapnik", "parameters": { "attribution": "(C) OpenStreetMap contributors", + "interpolate": false, "maxZoom": 19.0, "minZoom": 0.0, "provider": "OpenStreetMap", @@ -72,6 +74,7 @@ 37.936 ] ], + "interpolate": true, "path": "https://maplibre.org/maplibre-gl-js/docs/assets/radar.gif" }, "type": "ImageSource" diff --git a/examples/local.jGIS b/examples/local.jGIS index d7829e20b..2dccbd54f 100644 --- a/examples/local.jGIS +++ b/examples/local.jGIS @@ -7,6 +7,7 @@ "layers": { "042f37ac-6d05-4b07-a944-8f56fb889612": { "name": "Custom Shapefile Layer", + "opacity": 1.0, "parameters": { "color": { "fill-color": "#cc3342", @@ -15,7 +16,6 @@ "stroke-line-join": "round", "stroke-width": 1.25 }, - "opacity": 1.0, "source": "0e9d027f-56ea-427e-a845-a5ee33427e67", "symbologyState": { "renderType": "Single Symbol" @@ -27,8 +27,8 @@ }, "52e46caf-e212-4276-ade9-6b0683642f8d": { "name": "Custom Image Layer", + "opacity": 1.0, "parameters": { - "opacity": 1.0, "source": "f124c6de-e444-4798-be88-61c3ee8968db" }, "type": "ImageLayer", @@ -36,6 +36,7 @@ }, "f5e91204-3ce9-408e-b179-c6cf5f75f0f7": { "name": "OpenStreetMap.Mapnik Layer", + "opacity": 1.0, "parameters": { "source": "3bd6b874-a28a-4ef6-bef8-a7257bb35c51" }, @@ -74,6 +75,7 @@ "name": "OpenStreetMap.Mapnik", "parameters": { "attribution": "(C) OpenStreetMap contributors", + "interpolate": false, "maxZoom": 19.0, "minZoom": 0.0, "provider": "OpenStreetMap", @@ -103,6 +105,7 @@ 37.936 ] ], + "interpolate": false, "path": "radar.gif" }, "type": "ImageSource" diff --git a/examples/pmtiles-raster.jGIS b/examples/pmtiles-raster.jGIS index 21fc3da80..7786733a2 100644 --- a/examples/pmtiles-raster.jGIS +++ b/examples/pmtiles-raster.jGIS @@ -7,11 +7,11 @@ "2815540d-70c6-4eed-ba86-51596adf6863": { "name": "Custom Raster Layer", "parameters": { - "opacity": 0.5, "source": "d3a5ad67-d6fe-4793-a6c5-dd773d76c745" }, "type": "RasterLayer", - "visible": true + "visible": true, + "opacity": 0.5 }, "3d4563da-904d-4026-a06b-1e8cffbf536f": { "name": "OpenStreetMap.Mapnik Layer", diff --git a/examples/pmtiles-vector.jGIS b/examples/pmtiles-vector.jGIS index 16bde4f83..4b3f99314 100644 --- a/examples/pmtiles-vector.jGIS +++ b/examples/pmtiles-vector.jGIS @@ -16,12 +16,12 @@ "name": "Buildings", "parameters": { "color": "#FF0000", - "opacity": 1.0, "source": "e52daf20-9b57-4a14-9521-70eabbe4cda2", "type": "line" }, "type": "VectorTileLayer", - "visible": true + "visible": true, + "opacity": 1 } }, "metadata": {}, diff --git a/examples/roads.jGIS b/examples/roads.jGIS index 4e6468261..f472bbf7d 100644 --- a/examples/roads.jGIS +++ b/examples/roads.jGIS @@ -7451,7 +7451,6 @@ ] ] }, - "opacity": 1.0, "source": "b9e30ab1-864e-4395-9071-cddb6ac813e5", "symbologyState": { "colorRamp": "rainbow", @@ -7463,7 +7462,8 @@ "type": "line" }, "type": "VectorLayer", - "visible": true + "visible": true, + "opacity": 1.0 } }, "metadata": {}, diff --git a/examples/shapefile.jGIS b/examples/shapefile.jGIS index 9c905f073..dc79dadb6 100644 --- a/examples/shapefile.jGIS +++ b/examples/shapefile.jGIS @@ -22,7 +22,6 @@ "stroke-line-join": "round", "stroke-width": 1.25 }, - "opacity": 1.0, "source": "eb4b68da-eab6-44a1-8e75-ca9c871d2755", "symbologyState": { "renderType": "Single Symbol" @@ -30,7 +29,8 @@ "type": "line" }, "type": "VectorLayer", - "visible": true + "visible": true, + "opacity": 1 } }, "metadata": {}, diff --git a/examples/test.jGIS b/examples/test.jGIS index d70bd73da..98fad9f10 100644 --- a/examples/test.jGIS +++ b/examples/test.jGIS @@ -27,12 +27,12 @@ "name": "Regions France", "parameters": { "color": "#e66100", - "opacity": 0.6, "source": "7d082e75-69d5-447a-82d8-b05cca5945ba", "type": "line" }, "type": "VectorLayer", - "visible": true + "visible": true, + "opacity": 0.6 }, "a0044fd7-f167-445f-b3d1-620a8f94b498": { "name": "Open Topo Map", diff --git a/examples/world.jGIS b/examples/world.jGIS index 97deb6911..b4f8d732e 100644 --- a/examples/world.jGIS +++ b/examples/world.jGIS @@ -82,7 +82,6 @@ ] ] }, - "opacity": 1.0, "source": "b4287bea-e217-443c-b527-58f7559c824c", "symbologyState": { "colorRamp": "cool", @@ -95,7 +94,8 @@ "type": "fill" }, "type": "VectorLayer", - "visible": true + "visible": true, + "opacity": 1 }, "f80d0fa2-3e2b-4922-b7d5-fefd4b085259": { "name": "France", @@ -107,7 +107,6 @@ "stroke-line-join": "round", "stroke-width": 1.25 }, - "opacity": 1.0, "source": "5970d6c9-26be-4cc6-84c9-16593dd3edfe", "symbologyState": { "renderType": "Single Symbol" @@ -115,7 +114,8 @@ "type": "line" }, "type": "VectorLayer", - "visible": true + "visible": true, + "opacity": 1 } }, "metadata": {}, diff --git a/packages/base/src/dialogs/layerBrowserDialog.tsx b/packages/base/src/dialogs/layerBrowserDialog.tsx index e8af9cc3f..5a18f34f3 100644 --- a/packages/base/src/dialogs/layerBrowserDialog.tsx +++ b/packages/base/src/dialogs/layerBrowserDialog.tsx @@ -112,6 +112,7 @@ export const LayerBrowserComponent: React.FC = ({ source: sourceId, }, visible: true, + opacity: 1.0, name: tile.name + ' Layer', }; diff --git a/packages/base/src/formbuilder/creationform.tsx b/packages/base/src/formbuilder/creationform.tsx index 16f68db11..45a4cdc23 100644 --- a/packages/base/src/formbuilder/creationform.tsx +++ b/packages/base/src/formbuilder/creationform.tsx @@ -111,6 +111,14 @@ export class CreationForm extends React.Component { layerSchema['required'] = ['name', ...layerSchema['required']]; layerSchema['properties'] = { name: { type: 'string', description: 'The name of the layer' }, + opacity: { + type: 'number', + description: 'The opacity of the object', + default: 1, + multipleOf: 0.1, + minimum: 0, + maximum: 1, + }, ...layerSchema['properties'], }; } @@ -173,7 +181,7 @@ export class CreationForm extends React.Component { if (this.props.createLayer) { let actualName = ''; - const { name, ...layerData } = + const { name, opacity, ...layerData } = (await layerCreationPromise?.promise) as IDict; actualName = @@ -184,6 +192,7 @@ export class CreationForm extends React.Component { type: this.props.layerType || 'RasterLayer', parameters: layerData, visible: true, + opacity: opacity ?? 1, name: actualName, }; diff --git a/packages/base/src/formbuilder/editform.tsx b/packages/base/src/formbuilder/editform.tsx index 6560a1dc2..a2f13a791 100644 --- a/packages/base/src/formbuilder/editform.tsx +++ b/packages/base/src/formbuilder/editform.tsx @@ -53,11 +53,28 @@ export class EditForm extends React.Component { } LayerForm = getLayerTypeForm(layer?.type || 'RasterLayer'); - layerData = deepCopy(layer?.parameters || {}); + layerData = { + opacity: layer?.opacity ?? 1, + ...deepCopy(layer?.parameters || {}), + }; layerSchema = deepCopy( this.props.formSchemaRegistry.getSchemas().get(layer.type), ); + if (layerSchema) { + layerSchema['properties'] = { + ...layerSchema['properties'], + opacity: { + type: 'number', + description: 'The opacity of the source', + default: 1, + multipleOf: 0.1, + minimum: 0, + maximum: 1, + }, + }; + } + if (!layerSchema) { console.error(`Cannot find schema for ${layer.type}`); return; @@ -99,7 +116,28 @@ export class EditForm extends React.Component { schema={layerSchema} sourceData={layerData} syncData={(properties: { [key: string]: any }) => { - this.syncObjectProperties(this.props.layer, properties); + if (!this.props.layer) { + return; + } + + const { opacity, ...params } = properties; + + if (opacity !== undefined) { + const layer = this.props.model.getLayer(this.props.layer); + if (layer) { + this.props.model.sharedModel.updateLayer(this.props.layer, { + ...layer, + opacity, + }); + } + } + + if (Object.keys(params).length > 0) { + this.props.model.sharedModel.updateObjectParameters( + this.props.layer, + params, + ); + } }} /> diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index 82caddef6..7c37221ae 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -26,7 +26,6 @@ import { IShapefileSource, IStacLayer, IVectorLayer, - IVectorTileLayer, IVectorTileSource, IGeoParquetSource, IWebGlLayer, @@ -316,10 +315,10 @@ export class MainView extends React.Component { const layerModel: IJGISLayer = { type: 'VectorLayer', visible: true, + opacity: 1.0, name: 'Drag and Drop layer', parameters: { color: '#FF0000', - opacity: 1.0, type: 'line', source: sourceId, }, @@ -989,7 +988,7 @@ export class MainView extends React.Component { layerParameters = layer.parameters as IRasterLayer; newMapLayer = new TileLayer({ - opacity: layerParameters.opacity, + opacity: layer.opacity, visible: layer.visible, source: this._sources[layerParameters.source], }); @@ -1000,7 +999,7 @@ export class MainView extends React.Component { layerParameters = layer.parameters as IVectorLayer; newMapLayer = new VectorLayer({ - opacity: layerParameters.opacity, + opacity: layer.opacity, visible: layer.visible, source: this._sources[layerParameters.source], style: this.vectorLayerStyleRuleBuilder(layer), @@ -1012,7 +1011,7 @@ export class MainView extends React.Component { layerParameters = layer.parameters as IVectorLayer; newMapLayer = new VectorTileLayer({ - opacity: layerParameters.opacity, + opacity: layer.opacity, source: this._sources[layerParameters.source], style: this.vectorLayerStyleRuleBuilder(layer), }); @@ -1036,7 +1035,7 @@ export class MainView extends React.Component { layerParameters = layer.parameters as IImageLayer; newMapLayer = new ImageLayer({ - opacity: layerParameters.opacity, + opacity: layer.opacity, source: this._sources[layerParameters.source], }); @@ -1047,7 +1046,7 @@ export class MainView extends React.Component { // This is to handle python sending a None for the color const layerOptions: any = { - opacity: layerParameters.opacity, + opacity: layer.opacity, source: this._sources[layerParameters.source], }; @@ -1064,7 +1063,7 @@ export class MainView extends React.Component { layerParameters = layer.parameters as IHeatmapLayer; newMapLayer = new HeatmapLayer({ - opacity: layerParameters.opacity, + opacity: layer.opacity, source: this._sources[layerParameters.source], blur: layerParameters.blur ?? 15, radius: layerParameters.radius ?? 8, @@ -1079,7 +1078,7 @@ export class MainView extends React.Component { newMapLayer = new StacLayer({ displayPreview: true, data: layerParameters.data, - opacity: layerParameters.opacity, + opacity: layer.opacity, visible: layer.visible, assets: Object.keys(layerParameters.data.assets), extent: layerParameters.data.bbox, @@ -1316,13 +1315,11 @@ export class MainView extends React.Component { switch (layer.type) { case 'RasterLayer': { - mapLayer.setOpacity(layer.parameters?.opacity || 1); + mapLayer.setOpacity(layer.opacity || 1); break; } case 'VectorLayer': { - const layerParams = layer.parameters as IVectorLayer; - - mapLayer.setOpacity(layerParams.opacity || 1); + mapLayer.setOpacity(layer.opacity || 1); (mapLayer as VectorLayer).setStyle( this.vectorLayerStyleRuleBuilder(layer), @@ -1331,9 +1328,7 @@ export class MainView extends React.Component { break; } case 'VectorTileLayer': { - const layerParams = layer.parameters as IVectorTileLayer; - - mapLayer.setOpacity(layerParams.opacity || 1); + mapLayer.setOpacity(layer.opacity || 1); (mapLayer as VectorTileLayer).setStyle( this.vectorLayerStyleRuleBuilder(layer), @@ -1349,7 +1344,7 @@ export class MainView extends React.Component { break; } case 'WebGlLayer': { - mapLayer.setOpacity(layer.parameters?.opacity); + mapLayer.setOpacity(layer.opacity || 1); if (layer?.parameters?.color) { (mapLayer as WebGlTileLayer).setStyle({ @@ -1362,7 +1357,7 @@ export class MainView extends React.Component { const layerParams = layer.parameters as IHeatmapLayer; const heatmap = mapLayer as HeatmapLayer; - heatmap.setOpacity(layerParams.opacity ?? 1); + heatmap.setOpacity(layer.opacity ?? 1); heatmap.setBlur(layerParams.blur ?? 15); heatmap.setRadius(layerParams.radius ?? 8); heatmap.setGradient( @@ -1374,7 +1369,7 @@ export class MainView extends React.Component { break; } case 'StacLayer': - mapLayer.setOpacity(layer.parameters?.opacity || 1); + mapLayer.setOpacity(layer.opacity || 1); break; } } diff --git a/packages/base/src/processing/index.ts b/packages/base/src/processing/index.ts index 20b1942b9..d8f3f8759 100644 --- a/packages/base/src/processing/index.ts +++ b/packages/base/src/processing/index.ts @@ -244,6 +244,7 @@ export async function executeSQLProcessing( type: 'VectorLayer', parameters: { source: newSourceId }, visible: true, + opacity: 1.0, name: layerName, }; @@ -264,6 +265,7 @@ export async function executeSQLProcessing( type: 'VectorLayer', parameters: { source: newSourceId }, visible: true, + opacity: 1.0, name: layerName, }; diff --git a/packages/base/src/stacBrowser/hooks/useStacSearch.ts b/packages/base/src/stacBrowser/hooks/useStacSearch.ts index 5d07ace63..5c2062229 100644 --- a/packages/base/src/stacBrowser/hooks/useStacSearch.ts +++ b/packages/base/src/stacBrowser/hooks/useStacSearch.ts @@ -251,6 +251,7 @@ function useStacSearch({ model }: IUseStacSearchProps): IUseStacSearchReturn { type: 'StacLayer', parameters: { data: stacData }, visible: true, + opacity: 1.0, name: stacData.properties.title ?? stacData.id, }; diff --git a/packages/schema/src/schema/project/jgis.json b/packages/schema/src/schema/project/jgis.json index b710f415a..115d1e3b7 100644 --- a/packages/schema/src/schema/project/jgis.json +++ b/packages/schema/src/schema/project/jgis.json @@ -74,6 +74,14 @@ "type": "boolean", "default": true }, + "opacity": { + "type": "number", + "description": "The opacity of the source", + "default": 1, + "multipleOf": 0.1, + "minimum": 0, + "maximum": 1 + }, "parameters": { "type": "object" }, diff --git a/packages/schema/src/schema/project/layers/heatmapLayer.json b/packages/schema/src/schema/project/layers/heatmapLayer.json index 4231308a1..a37731dd2 100644 --- a/packages/schema/src/schema/project/layers/heatmapLayer.json +++ b/packages/schema/src/schema/project/layers/heatmapLayer.json @@ -9,14 +9,6 @@ "type": "string", "description": "The id of the source" }, - "opacity": { - "type": "number", - "description": "The opacity of the source", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 - }, "radius": { "type": "number", "description": "Radius size in pixels", diff --git a/packages/schema/src/schema/project/layers/imageLayer.json b/packages/schema/src/schema/project/layers/imageLayer.json index 39ac323f5..c50f35a63 100644 --- a/packages/schema/src/schema/project/layers/imageLayer.json +++ b/packages/schema/src/schema/project/layers/imageLayer.json @@ -8,14 +8,6 @@ "source": { "type": "string", "description": "The id of the source" - }, - "opacity": { - "type": "number", - "description": "The opacity of the source", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 } } } diff --git a/packages/schema/src/schema/project/layers/rasterLayer.json b/packages/schema/src/schema/project/layers/rasterLayer.json index c42509cba..00d70fec2 100644 --- a/packages/schema/src/schema/project/layers/rasterLayer.json +++ b/packages/schema/src/schema/project/layers/rasterLayer.json @@ -8,14 +8,6 @@ "source": { "type": "string", "description": "The id of the source" - }, - "opacity": { - "type": "number", - "description": "The opacity of the source", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 } } } diff --git a/packages/schema/src/schema/project/layers/stacLayer.json b/packages/schema/src/schema/project/layers/stacLayer.json index 7cb7d320c..c5aac9795 100644 --- a/packages/schema/src/schema/project/layers/stacLayer.json +++ b/packages/schema/src/schema/project/layers/stacLayer.json @@ -8,14 +8,6 @@ "data": { "type": "object", "description": "The data of the source" - }, - "opacity": { - "type": "number", - "description": "The opacity of the source", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 } } } diff --git a/packages/schema/src/schema/project/layers/vectorLayer.json b/packages/schema/src/schema/project/layers/vectorLayer.json index 992777ff1..31fc54cb0 100644 --- a/packages/schema/src/schema/project/layers/vectorLayer.json +++ b/packages/schema/src/schema/project/layers/vectorLayer.json @@ -13,14 +13,6 @@ "type": "object", "description": "The color of the the object" }, - "opacity": { - "type": "number", - "description": "The opacity of the the object", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 - }, "symbologyState": { "type": "object", "description": "The state of the symbology panel options", diff --git a/packages/schema/src/schema/project/layers/vectorTileLayer.json b/packages/schema/src/schema/project/layers/vectorTileLayer.json index 1fd9cc6fb..f4f52e5ec 100644 --- a/packages/schema/src/schema/project/layers/vectorTileLayer.json +++ b/packages/schema/src/schema/project/layers/vectorTileLayer.json @@ -12,14 +12,6 @@ "color": { "type": "object", "description": "The color of the the object" - }, - "opacity": { - "type": "number", - "description": "The opacity of the the object", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 } } } diff --git a/packages/schema/src/schema/project/layers/webGlLayer.json b/packages/schema/src/schema/project/layers/webGlLayer.json index d88213079..849ef2c7d 100644 --- a/packages/schema/src/schema/project/layers/webGlLayer.json +++ b/packages/schema/src/schema/project/layers/webGlLayer.json @@ -9,14 +9,6 @@ "type": "string", "description": "The id of the source" }, - "opacity": { - "type": "number", - "description": "The opacity of the source", - "default": 1, - "multipleOf": 0.1, - "minimum": 0, - "maximum": 1 - }, "color": { "oneOf": [ { "type": "string" }, diff --git a/python/jupytergis_core/jupytergis_core/jgis_ydoc.py b/python/jupytergis_core/jupytergis_core/jgis_ydoc.py index 10ca54912..22c65ea63 100644 --- a/python/jupytergis_core/jupytergis_core/jgis_ydoc.py +++ b/python/jupytergis_core/jupytergis_core/jgis_ydoc.py @@ -61,6 +61,16 @@ def set(self, value: str) -> None: if file_version > Version(SCHEMA_VERSION): raise ValueError(f"Cannot load file version {file_version}") + if file_version < Version("0.8.1"): + for _layer_id, layer in valueDict.get("layers", {}).items(): + params = layer.get("parameters", {}) + if "opacity" in params: + # only set top-level opacity if missing + if "opacity" not in layer: + layer["opacity"] = params["opacity"] + # remove from parameters + del params["opacity"] + with self._ydoc.transaction(): self._ylayers.clear() self._ylayers.update(valueDict.get("layers", {})) diff --git a/python/jupytergis_lab/jupytergis_lab/notebook/gis_document.py b/python/jupytergis_lab/jupytergis_lab/notebook/gis_document.py index 03319d0dd..d01bcf8c9 100644 --- a/python/jupytergis_lab/jupytergis_lab/notebook/gis_document.py +++ b/python/jupytergis_lab/jupytergis_lab/notebook/gis_document.py @@ -175,7 +175,8 @@ def add_raster_layer( "type": LayerType.RasterLayer, "name": name, "visible": True, - "parameters": {"source": source_id, "opacity": opacity}, + "opacity": opacity, + "parameters": {"source": source_id}, } return self._add_layer(OBJECT_FACTORY.create_layer(layer, self)) @@ -224,11 +225,10 @@ def add_vectortile_layer( "type": LayerType.VectorTileLayer, "name": name, "visible": True, + "opacity": opacity, "parameters": { "source": source_id, - "opacity": opacity, "color": color_expr, - "opacity": opacity, }, "filters": { "appliedFilters": [ @@ -299,10 +299,10 @@ def add_geojson_layer( "type": LayerType.VectorLayer, "name": name, "visible": True, + "opacity": opacity, "parameters": { "source": source_id, "color": color_expr, - "opacity": opacity, }, "filters": { "appliedFilters": [ @@ -345,7 +345,8 @@ def add_image_layer( "type": LayerType.ImageLayer, "name": name, "visible": True, - "parameters": {"source": source_id, "opacity": opacity}, + "opacity": opacity, + "parameters": {"source": source_id}, } return self._add_layer(OBJECT_FACTORY.create_layer(layer, self)) @@ -383,7 +384,8 @@ def add_video_layer( "type": LayerType.RasterLayer, "name": name, "visible": True, - "parameters": {"source": source_id, "opacity": opacity}, + "opacity": opacity, + "parameters": {"source": source_id}, } return self._add_layer(OBJECT_FACTORY.create_layer(layer, self)) @@ -428,9 +430,9 @@ def add_tiff_layer( "type": LayerType.WebGlLayer, "name": name, "visible": True, + "opacity": opacity, "parameters": { "source": source_id, - "opacity": opacity, "color": color_expr, }, } @@ -532,11 +534,11 @@ def add_heatmap_layer( "type": LayerType.HeatmapLayer, "name": name, "visible": True, + "opacity": opacity, "parameters": { "source": source_id, "type": type, "color": gradient, - "opacity": opacity, "blur": blur, "radius": radius, "feature": feature, @@ -583,10 +585,10 @@ def add_geoparquet_layer( "type": LayerType.VectorLayer, "name": name, "visible": True, + "opacity": opacity, "parameters": { "source": source_id, "type": type, - "opacity": opacity, "color": color_expr, }, "filters": { diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index 7d301bfe8..9a0cc79bc 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -298,6 +298,8 @@ def qgis_layer_to_jgis( if symbol: # Opacity handling opacity = symbol.opacity() + layer_parameters["opacity"] = opacity + alpha = hex(int(opacity * 255))[2:].zfill(2) if isinstance(symbol, QgsMarkerSymbol): @@ -390,13 +392,13 @@ def qgis_layer_to_jgis( source_name = f"{layer_name} Source" layer_parameters["source"] = source_id - layer_parameters["opacity"] = layer.opacity() layers[layer_id] = { "name": layer_name, "parameters": layer_parameters, "type": layer_type, "visible": is_visible, + "opacity": layer.opacity(), } sources[source_id] = { "name": source_name, @@ -517,6 +519,9 @@ def import_project_from_qgis(path: str | Path): def get_base_symbol(geometry_type, color_params, opacity): """Returns a base symbol based on geometry type.""" + if opacity is None: + opacity = 1.0 + if geometry_type == "circle": symbol = QgsMarkerSymbol() elif geometry_type == "line": @@ -786,7 +791,7 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: if geometry_type == "circle": symbol = QgsMarkerSymbol() color_params = layer_params.get("color", {}) - opacity = layer_params.get("opacity", 1.0) + opacity = layer.get("opacity", 1.0) symbology_state = layer_params.get("symbologyState", {}) render_type = symbology_state.get("renderType", "Single Symbol") @@ -816,7 +821,7 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: symbol.setOutputUnit(Qgis.RenderUnit.Pixels) color_params = layer_params.get("color", {}) - opacity = layer_params.get("opacity") + opacity = layer.get("opacity") symbology_state = layer_params.get("symbologyState", {}) render_type = symbology_state.get("renderType", "Single Symbol") @@ -844,7 +849,7 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: symbol = QgsFillSymbol() symbol.setOutputUnit(Qgis.RenderUnit.Pixels) color_params = layer_params.get("color", {}) - opacity = layer_params.get("opacity", 1.0) + opacity = layer.get("opacity", 1.0) symbology_state = layer_params.get("symbologyState", {}) render_type = symbology_state.get("renderType", "Single Symbol") @@ -985,7 +990,7 @@ def build_uri(parameters: dict[str, str], source_type: str) -> str | None: return map_layer.setId(layer_id) - map_layer.setOpacity(layer.get("parameters", {}).get("opacity", 1.0)) + map_layer.setOpacity(layer.get("opacity", 1.0)) # Map the source id/name to the layer layerSourceMap = settings.value("layerSourceMap", {}) diff --git a/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py b/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py index 377badc39..beb4a1f9b 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py +++ b/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py @@ -32,38 +32,38 @@ def test_qgis_loader(): "_02b1b4d5_316b_4f4d_9c38_16bf10a3bcb8": { "name": "OpenStreetMap0", "parameters": { - "opacity": 1.0, "source": source_id0, }, "type": "RasterLayer", "visible": True, + "opacity": 1.0, }, "_097deeeb_6564_48d1_a3be_1caa4d93382f": { "name": "OpenStreetMap1", "parameters": { - "opacity": 1.0, "source": source_id1, }, "type": "RasterLayer", "visible": True, + "opacity": 1.0, }, "_bccce044_998d_45f9_bf6b_fe1472681cc3": { "name": "OpenStreetMap2", "parameters": { - "opacity": 1.0, "source": source_id2, }, "type": "RasterLayer", "visible": True, + "opacity": 1.0, }, "_32a77a2c_1756_4876_9f99_e3c7b702f86a": { "name": "OpenStreetMap3", "parameters": { - "opacity": 1.0, "source": source_id3, }, "type": "RasterLayer", "visible": True, + "opacity": 1.0, }, }, layerTree=[ @@ -120,6 +120,21 @@ def test_qgis_loader(): ) +def normalize_jgis(jgis): + import copy + + norm = copy.deepcopy(jgis) + + for layer in norm["layers"].values(): + if "opacity" not in layer: + layer["opacity"] = 1.0 + + if "parameters" in layer and "opacity" in layer["parameters"]: + del layer["parameters"]["opacity"] + + return norm + + def test_qgis_saver(): filename = FILES / "project1.qgz" if os.path.exists(filename): @@ -160,25 +175,24 @@ def test_qgis_saver(): layer_ids[0]: { "name": "OpenStreetMap0", "parameters": { - "opacity": 1.0, "source": source_ids[0], }, "type": "RasterLayer", "visible": True, + "opacity": 1.0, }, layer_ids[1]: { "name": "OpenStreetMap1", "parameters": { - "opacity": 1.0, "source": source_ids[1], }, "type": "RasterLayer", "visible": True, + "opacity": 1.0, }, layer_ids[2]: { "name": "Vector Tile Layer", "parameters": { - "opacity": 1.0, "color": { "circle-fill-color": "#e1598987", "circle-stroke-color": "#e1598987", @@ -190,15 +204,16 @@ def test_qgis_saver(): }, "type": "VectorTileLayer", "visible": True, + "opacity": 1.0, }, layer_ids[3]: { "name": "OpenStreetMap3", "parameters": { - "opacity": 1.0, "source": source_ids[3], }, "type": "RasterLayer", "visible": False, + "opacity": 1.0, }, layer_ids[4]: { "name": "Custom GeoJSON Layer", @@ -207,13 +222,13 @@ def test_qgis_saver(): "fill-color": "#4ea4d0", "stroke-color": "#4ea4d0", }, - "opacity": 1.0, "source": source_ids[4], "symbologyState": {"renderType": "Single Symbol"}, "type": "fill", }, "type": "VectorLayer", "visible": True, + "opacity": 1.0, }, layer_ids[5]: { "name": "Custom GeoJSON Layer", @@ -244,7 +259,6 @@ def test_qgis_saver(): ], "stroke-color": "#000000", }, - "opacity": 1.0, "source": source_ids[5], "symbologyState": { "renderType": "Graduated", @@ -254,6 +268,7 @@ def test_qgis_saver(): }, "type": "VectorLayer", "visible": True, + "opacity": 1.0, }, layer_ids[6]: { "name": "Custom GeoJSON Layer", @@ -299,7 +314,6 @@ def test_qgis_saver(): "stroke-line-join": "bevel", "stroke-width": 1.0, }, - "opacity": 1.0, "source": source_ids[6], "symbologyState": { "colorRamp": "viridis", @@ -312,6 +326,7 @@ def test_qgis_saver(): }, "type": "VectorLayer", "visible": True, + "opacity": 1.0, }, }, "layerTree": [ @@ -394,4 +409,4 @@ def test_qgis_saver(): imported_jgis = import_project_from_qgis(filename) - assert jgis == imported_jgis + assert normalize_jgis(jgis) == normalize_jgis(imported_jgis)