Skip to content

Commit 584a43e

Browse files
committed
PB-95: Added zoom to KML extent when importing KML file
1 parent 1cbce9f commit 584a43e

File tree

13 files changed

+125
-12
lines changed

13 files changed

+125
-12
lines changed

src/api/layers/WMSCapabilitiesParser.class.js

+9
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@ export default class WMSCapabilitiesParser {
208208
}
209209

210210
_getLayerExtent(layerId, layer, parents, projection) {
211+
// TODO PB-243 handling of extent out of projection bound (currently not properly handled)
212+
// - extent totally out of projection bounds
213+
// => return null and set outOfBounds flag to true
214+
// - extent totally inside of projection bounds
215+
// => crop extent and set outOfBounds flag to true
216+
// - extent partially inside projection bounds
217+
// => take intersect extent and set outOfBounds flag to true
218+
// - no extent
219+
// => return null and set the outOfBounds flag to false (we don't know)
211220
let layerExtent = null
212221
const matchedBbox = layer.BoundingBox?.find((bbox) => bbox.crs === projection.epsg)
213222
// First try to find a matching extent from the BoundingBox

src/api/layers/WMTSCapabilitiesParser.class.js

+1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export default class WMTSCapabilitiesParser {
162162
}
163163

164164
_getLayerExtent(layerId, layer, projection) {
165+
// TODO PB-243 handling of extent out of projection bound (currently not properly handled)
165166
let layerExtent = null
166167
let extentEpsg = null
167168
// First try to get the extent from the default bounding box

src/modules/i18n/locales/de.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,5 @@
648648
"no_result": "Kein Ergebnis",
649649
"loading": "Laden",
650650
"search_in_catalogue_placeholder": "Suche in importierten Karten",
651-
"kml_name": "Name der KML",
652-
"name_too_long": "Name ist zu lang"
651+
"kml_gpx_file_out_of_bounds": "Der Dateiinhalt liegt außerhalb der Projektionsgrenzen"
653652
}

src/modules/i18n/locales/en.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,5 @@
648648
"no_result": "No result",
649649
"loading": "Loading",
650650
"search_in_catalogue_placeholder": "Search in imported maps",
651-
"kml_name": "Name of the KML",
652-
"name_too_long": "Name is too long"
651+
"kml_gpx_file_out_of_bounds": "File content is out of projection bounds"
653652
}

src/modules/i18n/locales/fr.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,5 @@
648648
"no_result": "Pas de résultat",
649649
"loading": "Chargement",
650650
"search_in_catalogue_placeholder": "Rechercher dans les cartes importées",
651-
"kml_name": "Nom du KML",
652-
"name_too_long": "Le nom est trop long"
651+
"kml_gpx_file_out_of_bounds": "Le contenu du fichier est hors des limites de projection"
653652
}

src/modules/i18n/locales/it.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,5 @@
648648
"no_result": "Nessun risultato",
649649
"loading": "Caricamento",
650650
"search_in_catalogue_placeholder": "Cerca nelle mappe importate",
651-
"kml_name": "Nome del KML",
652-
"name_too_long": "Il nome è troppo lungo"
651+
"kml_gpx_file_out_of_bounds": "Il contenuto del file è fuori dai limiti della proiezione"
653652
}

src/modules/i18n/locales/rm.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,5 @@
646646
"no_result": "Nagut resultat",
647647
"loading": "Chargiada",
648648
"search_in_catalogue_placeholder": "Cercar en mapas importads",
649-
"kml_name": "Num da la KML",
650-
"name_too_long": "Il num è massa lung"
649+
"kml_gpx_file_out_of_bounds": "Il contetn dal file è en furma da las limitas da projezziun"
651650
}

src/modules/menu/components/LayerCatalogueItem.vue

+10
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ function transformExtentIntoPolygon(flattenExtent) {
151151
152152
const lv95Extent = [LV95.bounds.bottomLeft, LV95.bounds.topRight]
153153
function zoomToLayer() {
154+
// TODO PB-243 better handling of layers extent errors
155+
// - extent totally out of projection bounds
156+
// => layer should be marked as out of bounds and disabled, no zoom to layer icon
157+
// but an error icon with reason
158+
// - extent totally inside of projection bounds
159+
// => current behavior, maybe add a warning icon about partial layer display
160+
// - extent partially inside projection bounds
161+
// => take intersection as extent, maybe add a warning icon about partial layer display
162+
// - no extent
163+
// => add a warning that the layer might be out of bound
154164
log.debug(`Zoom to layer ${item.value.name}`, item.value.extent)
155165
// Only zooming to layer's extent if its extent is entirely within LV95 extent.
156166
// If part (or all) of the extent is outside LV95 extent, we zoom to LV95 extent instead.

src/modules/menu/components/advancedTools/ImportFile/ImportFileLocalTab.vue

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useStore } from 'vuex'
66
import ImportFileButtons from '@/modules/menu/components/advancedTools/ImportFile/ImportFileButtons.vue'
77
import { handleFileContent } from '@/modules/menu/components/advancedTools/ImportFile/utils'
88
import { useImportButton } from '@/modules/menu/components/advancedTools/useImportButton'
9+
import { OutOfBoundsError } from '@/utils/coordinates/coordinateUtils'
910
import log from '@/utils/logging'
1011
1112
const LOCAL_UPLOAD_ACCEPT = '.kml,.KML,.gpx,.GPX'
@@ -72,8 +73,12 @@ async function loadFile() {
7273
handleFileContent(store, content, selectedFile.value.name)
7374
layerAdded.value = true
7475
} catch (error) {
75-
errorMessage.value = 'invalid_kml_gpx_file_error'
76-
log.error(`Failed to load file`, error)
76+
if (error instanceof OutOfBoundsError) {
77+
errorMessage.value = 'kml_gpx_file_out_of_bounds'
78+
} else {
79+
errorMessage.value = 'invalid_kml_gpx_file_error'
80+
log.error(`Failed to load file`, error)
81+
}
7782
}
7883
}
7984

src/modules/menu/components/advancedTools/ImportFile/ImportFileOnlineTab.vue

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useStore } from 'vuex'
66
import ImportFileButtons from '@/modules/menu/components/advancedTools/ImportFile/ImportFileButtons.vue'
77
import { handleFileContent } from '@/modules/menu/components/advancedTools/ImportFile/utils'
88
import TextInput from '@/utils/components/TextInput.vue'
9+
import { OutOfBoundsError } from '@/utils/coordinates/coordinateUtils'
910
import log from '@/utils/logging'
1011
import { isValidUrl } from '@/utils/utils'
1112
@@ -74,6 +75,8 @@ async function loadFile() {
7475
if (error instanceof AxiosError || /fetch/.test(error.message)) {
7576
log.error(`Failed to load file from url ${fileUrl.value}`, error)
7677
urlError.value = 'loading_error_network_failure'
78+
} else if (error instanceof OutOfBoundsError) {
79+
urlError.value = 'kml_gpx_file_out_of_bounds'
7780
} else {
7881
log.error(`Failed to parse file from url ${fileUrl.value}`, error)
7982
urlError.value = 'invalid_kml_gpx_file_error'

src/modules/menu/components/advancedTools/ImportFile/utils.js

+24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
import { getIntersection, isEmpty } from 'ol/extent'
2+
13
import KMLLayer from '@/api/layers/KMLLayer.class'
4+
import { WGS84 } from '@/utils/coordinates/coordinateSystems'
5+
import { normalizeExtent, OutOfBoundsError, projExtent } from '@/utils/coordinates/coordinateUtils'
6+
import { getKmlExtent } from '@/utils/kmlUtils'
7+
import log from '@/utils/logging'
28

39
/**
410
* Checks if file is KML
@@ -32,6 +38,24 @@ export function handleFileContent(store, content, source) {
3238
let layer = null
3339
if (isKml(content)) {
3440
layer = new KMLLayer(source, true, 1.0, null /* adminId */, content)
41+
const extent = getKmlExtent(content)
42+
if (extent?.length) {
43+
const projectionBounds = store.state.position.projection.getBoundsAs(WGS84).flatten
44+
let intersectExtent = getIntersection(projectionBounds, extent)
45+
if (!isEmpty(intersectExtent)) {
46+
log.debug(
47+
`Zoom to KML layer, layerExtent=${extent}, projectionBounds=${projectionBounds}, intersectExtent=${intersectExtent}`
48+
)
49+
store.dispatch(
50+
'zoomToExtent',
51+
normalizeExtent(
52+
projExtent(WGS84, store.state.position.projection, intersectExtent)
53+
)
54+
)
55+
} else {
56+
throw new OutOfBoundsError(`KML out of projection bounds: ${extent}`)
57+
}
58+
}
3559
store.dispatch('addLayer', layer)
3660
} else if (isGpx(content)) {
3761
// TODO GPX layer not done yet

src/utils/coordinates/coordinateUtils.js

+44
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,47 @@ export function projExtent(fromProj, toProj, extent) {
8282
}
8383
return null
8484
}
85+
86+
/**
87+
* Return an extent normalized to [[x, y], [x, y]] from a flat extent
88+
*
89+
* @param {Array} extent Extent to normalize
90+
* @returns {Array} Extent in the form [[x, y], [x, y]]
91+
*/
92+
export function normalizeExtent(extent) {
93+
let extentNormalized = extent
94+
if (extent?.length === 4) {
95+
// convert to the flat extent to [[x, y], [x, y]]
96+
extentNormalized = [
97+
[extent[0], extent[1]],
98+
[extent[2], extent[3]],
99+
]
100+
} else if (extent?.length !== 2) {
101+
throw new Error(`Invalid extent: ${extent}`)
102+
}
103+
return extentNormalized
104+
}
105+
106+
/**
107+
* Flatten extent
108+
*
109+
* @param {Array} extent Extent to flatten
110+
* @returns {Array} Flatten extent in from [minx, miny, maxx, maxy]
111+
*/
112+
export function flattenExtent(extent) {
113+
let flattenExtent = extent
114+
if (extent?.length === 2) {
115+
flattenExtent = [...extent[0], ...extent[1]]
116+
} else if (extent?.length !== 4) {
117+
throw new Error(`Invalid extent: ${extent}`)
118+
}
119+
return flattenExtent
120+
}
121+
122+
/** Coordinates or extent out of bounds error */
123+
export class OutOfBoundsError extends Error {
124+
constructor(message) {
125+
super(message)
126+
this.name = 'OutOfBoundsError'
127+
}
128+
}

src/utils/kmlUtils.js

+22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import { createEmpty as emptyExtent, extend as extendExtent } from 'ol/extent'
12
import KML from 'ol/format/KML'
23

4+
import { WGS84 } from '@/utils/coordinates/coordinateSystems'
5+
36
/**
47
* Read the KML name
58
*
@@ -34,3 +37,22 @@ export async function updateKmlActiveLayer(store, kmlLayer, data = null, metadat
3437
await store.dispatch('updateLayer', updateObj)
3538
}
3639
}
40+
41+
/**
42+
* Get KML extent
43+
*
44+
* @param {string} conetnt KML content
45+
* @returns {ol/extent} KML layer extent in WGS84 projection
46+
*/
47+
export function getKmlExtent(content) {
48+
const kml = new KML()
49+
const features = kml.readFeatures(content, {
50+
dataProjection: WGS84.epsg, // KML files should always be in WGS84
51+
featureProjection: WGS84.epsg,
52+
})
53+
const extent = emptyExtent()
54+
features.forEach((feature) => {
55+
extendExtent(extent, feature.getGeometry().getExtent())
56+
})
57+
return extent
58+
}

0 commit comments

Comments
 (0)