From 8c05d8d4247b06275068088853b3efc0de0ff416 Mon Sep 17 00:00:00 2001 From: Greg Mooney Date: Tue, 26 Nov 2024 15:52:36 +0100 Subject: [PATCH] Add support for other projections (#199) * Add support for other projections * Move def and register * Read projection from qgz * Add projection to qgis test --- package.json | 7 ++- packages/base/src/mainview/mainView.tsx | 55 +++++++++++++++---- .../jupytergis_qgis/qgis_loader.py | 1 + .../jupytergis_qgis/tests/test_qgis.py | 2 + yarn.lock | 27 +++++++++ 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 4aaa1263..f57af316 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "@jupyterlab/services": " ^7.0.0" }, "devDependencies": { + "@types/colormap": "^2.3.4", + "@types/proj4": "^2.5.5", "@types/webpack-env": "^1.18.5", "@typescript-eslint/eslint-plugin": "5.55.0", "@typescript-eslint/parser": "5.55.0", @@ -76,11 +78,12 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "latest", - "@types/colormap": "^2.3.4", "colormap": "^2.3.2", "geojson-vt": "^4.0.2", "geotiff": "^2.1.3", "ol": "^10.1.0", - "pmtiles": "^3.0.7" + "pmtiles": "^3.0.7", + "proj4": "^2.14.0", + "proj4-list": "^1.0.2" } } diff --git a/packages/base/src/mainview/mainView.tsx b/packages/base/src/mainview/mainView.tsx index cf392432..6b194467 100644 --- a/packages/base/src/mainview/mainView.tsx +++ b/packages/base/src/mainview/mainView.tsx @@ -51,12 +51,18 @@ import { import Static from 'ol/source/ImageStatic'; //@ts-expect-error no types for ol-pmtiles import { PMTilesRasterSource, PMTilesVectorSource } from 'ol-pmtiles'; +import { register } from 'ol/proj/proj4.js'; +import { get as getProjection } from 'ol/proj.js'; + import { Rule } from 'ol/style/flat'; +import proj4 from 'proj4'; import * as React from 'react'; import shp from 'shpjs'; import { isLightTheme } from '../tools'; import { MainViewModel } from './mainviewmodel'; import { Spinner } from './spinner'; +//@ts-expect-error no types for proj4-list +import proj4list from 'proj4-list'; interface IProps { viewModel: MainViewModel; @@ -75,6 +81,9 @@ export class MainView extends React.Component { constructor(props: IProps) { super(props); + proj4.defs(Array.from(proj4list)); + register(proj4); + this._mainViewModel = this.props.viewModel; this._mainViewModel.viewSettingChanged.connect(this._onViewChanged, this); this._model = this._mainViewModel.jGISModel; @@ -591,7 +600,6 @@ export class MainView extends React.Component { } newMapLayer = new WebGlTileLayer(layerOptions); - break; } } @@ -843,27 +851,52 @@ export class MainView extends React.Component { } } - private updateOptions(options: IJGISOptions) { - const view = this._Map.getView(); + private async updateOptions(options: IJGISOptions): Promise { + const { + projection, + extent, + useExtent, + latitude, + longitude, + zoom, + bearing + } = options; + + let view = this._Map.getView(); + const currentProjection = view.getProjection().getCode(); + + // Need to recreate view if the projection changes + if (currentProjection !== projection) { + const newProjection = getProjection(projection); + if (newProjection) { + view = new View({ projection: newProjection }); + } else { + console.warn(`Invalid projection: ${projection}`); + return; + } + } // Use the extent only if explicitly requested (QGIS files). - if (options.extent && options.useExtent) { - view.fit(options.extent); + if (useExtent && extent) { + view.fit(extent); } else { const centerCoord = fromLonLat( - [options.longitude || 0, options.latitude || 0], - this._Map.getView().getProjection() + [longitude || 0, latitude || 0], + view.getProjection() ); - this._Map.getView().setZoom(options.zoom || 0); - this._Map.getView().setCenter(centerCoord); + view.setCenter(centerCoord); + view.setZoom(zoom || 0); // Save the extent if it does not exists, to allow proper export to qgis. - if (options.extent === undefined) { + if (!options.extent) { options.extent = view.calculateExtent(); this._model.setOptions(options); } } - view.setRotation(options.bearing || 0); + + view.setRotation(bearing || 0); + + this._Map.setView(view); } private _onViewChanged( diff --git a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py index 76c6b365..eab6e4c2 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py +++ b/python/jupytergis_qgis/jupytergis_qgis/qgis_loader.py @@ -369,6 +369,7 @@ def import_project_from_qgis(path: str | Path): map_extent.yMaximum(), ], "useExtent": True, + "projection": project.crs().authid(), }, **jgis_layer_tree, } diff --git a/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py b/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py index e786a4e2..3d2901af 100644 --- a/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py +++ b/python/jupytergis_qgis/jupytergis_qgis/tests/test_qgis.py @@ -19,6 +19,7 @@ def test_qgis_loader(): options={ "bearing": 0.0, "pitch": 0, + "projection": "EPSG:3857", "extent": [ -25164292.70393259, -15184674.291019961, @@ -130,6 +131,7 @@ def test_qgis_saver(): "options": { "bearing": 0.0, "pitch": 0, + "projection": "EPSG:3857", "extent": [ -25164292.70393259, -15184674.291019961, diff --git a/yarn.lock b/yarn.lock index 8764c5bc..65deb9be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1096,6 +1096,7 @@ __metadata: "@fortawesome/free-solid-svg-icons": ^6.5.2 "@fortawesome/react-fontawesome": latest "@types/colormap": ^2.3.4 + "@types/proj4": ^2.5.5 "@types/webpack-env": ^1.18.5 "@typescript-eslint/eslint-plugin": 5.55.0 "@typescript-eslint/parser": 5.55.0 @@ -1111,6 +1112,8 @@ __metadata: ol: ^10.1.0 pmtiles: ^3.0.7 prettier: ^3.0.0 + proj4: ^2.14.0 + proj4-list: ^1.0.2 rimraf: ^3.0.2 typescript: ^5 webpack: ^5.76.3 @@ -3779,6 +3782,13 @@ __metadata: languageName: node linkType: hard +"@types/proj4@npm:^2.5.5": + version: 2.5.5 + resolution: "@types/proj4@npm:2.5.5" + checksum: 06a1898ffff6d7b4a0da04249b431b129ae31c1a75decfdcbdde6b6d7a7cf9560f8506e89f441b6ebe287e66ef5d0fd3e7ca33f9eb30c1427f8b9989bd3dc578 + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.13 resolution: "@types/prop-types@npm:15.7.13" @@ -10160,6 +10170,13 @@ __metadata: languageName: node linkType: hard +"proj4-list@npm:^1.0.2": + version: 1.0.2 + resolution: "proj4-list@npm:1.0.2" + checksum: 8705bfb92b7572c514d98944e148a6ab8891dc69aa49388677a999180d46d374758ddf0b733068e242b1454de2565c9e9bfcfd1c6b035d93834a0a879cd696d8 + languageName: node + linkType: hard + "proj4@npm:^2.1.4": version: 2.12.1 resolution: "proj4@npm:2.12.1" @@ -10170,6 +10187,16 @@ __metadata: languageName: node linkType: hard +"proj4@npm:^2.14.0": + version: 2.14.0 + resolution: "proj4@npm:2.14.0" + dependencies: + mgrs: 1.0.0 + wkt-parser: ^1.3.3 + checksum: 65c9e67658a89a152c002b5a512f25efeca3bf317c448868327ff16b77a8015c9315e4ce1a069e3a2dedd1acdd6c426f5e12d4a4fa7770f7f2c4ffb8675c3d1f + languageName: node + linkType: hard + "promise-inflight@npm:^1.0.1": version: 1.0.1 resolution: "promise-inflight@npm:1.0.1"