Skip to content

Commit

Permalink
Merge pull request #1066 from geoadmin/feat-PB-1000-geotiff-support
Browse files Browse the repository at this point in the history
PB-1000 : add support for local GeoTIFF import
  • Loading branch information
pakb authored Sep 19, 2024
2 parents 228cdf8 + dbcc4e7 commit 874029c
Show file tree
Hide file tree
Showing 19 changed files with 243 additions and 51 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"file-saver": "^2.0.5",
"form-data": "^4.0.0",
"geographiclib-geodesic": "^2.1.1",
"geotiff": "^2.1.3",
"hammerjs": "^2.0.8",
"jquery": "^3.7.1",
"jszip": "^3.10.1",
Expand Down
58 changes: 58 additions & 0 deletions src/api/layers/GeoTIFFLayer.class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import AbstractLayer, { LayerAttribution } from '@/api/layers/AbstractLayer.class'
import { InvalidLayerDataError } from '@/api/layers/InvalidLayerData.error'
import LayerTypes from '@/api/layers/LayerTypes.enum'

/**
* Metadata for an external Cloud-Optimized GeoTIFF layer
*
* @WARNING DON'T USE GETTER AND SETTER ! Instances of this class will be used a Vue 3 reactive
* object which SHOULD BE plain javascript object ! For convenience we use class instances but this
* has some limitations and javascript class getter and setter are not correctly supported which
* introduced subtle bugs. As rule of thumb we should avoid any public methods with side effects on
* properties, properties should change be changed either by the constructor or directly by setting
* them, not through a functions that updates other properties as it can lead to subtle bugs due
* to Vue reactivity engine.
*/
export default class GeoTIFFLayer extends AbstractLayer {
/**
* @param {String} geoTIFFConfig.fileSource The URL to access the GeoTIFF data.
* @param {Boolean} [geoTIFFConfig.visible=true] If the layer is visible on the map (or hidden).
* When `null` is given, then it uses the default value. Default is `true`
* @param {Number} [geoTIFFConfig.opacity=1.0] The opacity of this layer, between 0.0
* (transparent) and 1.0 (opaque). When `null` is given, then it uses the default value.
* Default is `1.0`
* @param {String | null} [geoTIFFConfig.data=null] Data/content of the GeoTIFF file, as a
* string. Default is `null`
* @throws InvalidLayerDataError if no `geoTIFFConfig` is given or if it is invalid
*/
constructor(geoTIFFConfig) {
if (!geoTIFFConfig) {
throw new InvalidLayerDataError('Missing GeoTIFF layer data', geoTIFFConfig)
}
const { fileSource = null, visible = true, opacity = 1.0, data = null } = geoTIFFConfig
if (fileSource === null) {
throw new InvalidLayerDataError('Missing GeoTIFF file source', geoTIFFConfig)
}
const isLocalFile = !fileSource.startsWith('http')
const attributionName = isLocalFile ? fileSource : new URL(fileSource).hostname
const fileName = isLocalFile
? fileSource
: fileSource.substring(fileSource.lastIndexOf('/') + 1)
super({
name: fileName,
id: fileSource,
type: LayerTypes.GEOTIFF,
baseUrl: fileSource,
opacity: opacity ?? 1.0,
visible: visible ?? true,
attributions: [new LayerAttribution(attributionName)],
isExternal: true,
hasDescription: false,
hasLegend: false,
})
this.isLocalFile = isLocalFile
this.fileSource = fileSource
this.isLoading = false
this.data = data
}
}
1 change: 1 addition & 0 deletions src/api/layers/LayerTypes.enum.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ const LayerTypes = {
GPX: 'GPX',
VECTOR: 'VECTOR',
GROUP: 'GROUP',
GEOTIFF: 'GEOTIFF',
}
export default LayerTypes
10 changes: 5 additions & 5 deletions src/modules/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"drawing_attached": "Zeichnung als Anhang hinzugefügt",
"drawing_too_large": "Ihre Zeichnung ist zu gross, entfernen Sie einige Details.",
"drop_invalid_url": "URL ist ungültig.",
"drop_me_here": "KML Datei hierhin ziehen",
"drop_me_here": "Datei hier ablegen (KML, KMZ, GPX, GeoTIFF)",
"duplicate_layer": "Karte duplizieren",
"east": "Ost",
"ech": "Geokatalog",
Expand Down Expand Up @@ -293,22 +293,23 @@
"import": "Importieren",
"import_file": "Datei importieren",
"import_file_succeeded": "Erfolg",
"import_file_tooltip": "Importieren eine externe KML- oder GPX-Datei",
"import_file_url_placeholder": "GPX KML URL",
"import_file_tooltip": "Importieren eine externe KML, KMZ, GPX oder GeoTIFF-Datei",
"import_file_url_placeholder": "GPX KML KMZ GeoTIFF URL",
"import_kml": "KML Import",
"import_maps": "Karten importieren",
"import_maps_tooltip": "Importieren Sie externe WMTS- oder WMS-Datenquellen",
"import_maps_url_placeholder": "WMTS WMS URL",
"import_online_placeholder": "WMTS WMS GPX KML URL",
"import_tooltip": "Importieren Sie externe WMTS-, WMS-, GPX- oder KML-Datenquellen",
"import_wms": "WMS Import",
"imported_file_out_of_bounds": "Der Dateiinhalt liegt ausserhalb der Projektionsgrenzen",
"inform_draw_rectangl_ctrl": "Mittels CTRL + Rechteck (cmd für Mac) über die Karte ziehen, lassen sich mehrere Objekte auf einmal räumlich abfragen.",
"inspire": "INSPIRE",
"inspire_service_link_href": "https://www.geo.admin.ch/de/home.html",
"inspire_service_link_label": "geo.admin.ch",
"invalid_email": "ungültige E-Mail",
"invalid_file": "Datei ungültig",
"invalid_kml_gpx_file_error": "Ungültige Datei, nur KML- oder GPX-Dateien werden unterstützt",
"invalid_import_file_error": "Ungültige Datei, nur KML, KMZ, GPX oder GeoTIFF-Dateien werden unterstützt",
"invalid_url": "URL ist ungültig.",
"invalid_wms_capabilities": "Ungültige WMS-Capabilities-Daten",
"invalid_wmts_capabilities": "Ungültige WMTS-Capabilities-Daten",
Expand All @@ -325,7 +326,6 @@
"kgs_service_link_label": "Kulturgüterschutz",
"kml": "KML",
"kml_gpx_file_empty": "Die KML/GPX-Datei ist leer",
"kml_gpx_file_out_of_bounds": "Der Dateiinhalt liegt ausserhalb der Projektionsgrenzen",
"kml_icon_url_cors_issue": "Die KML-Datei „{layerName}“ enthält Symbol(e) mit der URL {url}, die keine CORS-Unterstützung bietet! Bitte wenden Sie sich an den Administrator dieser URL, um die Unterstützung für CORS hinzuzufügen.",
"kml_no_text_elements": "Information: Die Labels werden nicht gespeichert",
"kml_style_default": "Standard",
Expand Down
10 changes: 5 additions & 5 deletions src/modules/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"drawing_attached": "Drawing added as attachment",
"drawing_too_large": "Your drawing is too large, remove some features",
"drop_invalid_url": "URL is not valid.",
"drop_me_here": "Drop KML file here",
"drop_me_here": "Drop file here (KML, KMZ, GPX, GeoTIFF)",
"duplicate_layer": "Duplicate map",
"east": "East",
"ech": "Geocatalog",
Expand Down Expand Up @@ -293,22 +293,23 @@
"import": "Import",
"import_file": "Import file",
"import_file_succeeded": "Success",
"import_file_tooltip": "Import an external KML or GPX file",
"import_file_url_placeholder": "GPX KML URL",
"import_file_tooltip": "Import an external KML, KMZ, GPX or GeoTIFF file",
"import_file_url_placeholder": "GPX KML KMZ GeoTIFF URL",
"import_kml": "KML import",
"import_maps": "Import maps",
"import_maps_tooltip": "Import external WMTS WMS sources",
"import_maps_url_placeholder": "WMTS WMS URL",
"import_online_placeholder": "WMTS WMS GPX KML URL",
"import_tooltip": "Import external WMTS WMS GPX KML sources",
"import_wms": "WMS import",
"imported_file_out_of_bounds": "File content is out of projection bounds",
"inform_draw_rectangl_ctrl": "To select several objects use CTRL (cmd on Mac) and draw a rectangle on the map.",
"inspire": "INSPIRE",
"inspire_service_link_href": "https://www.geo.admin.ch/en/home.html",
"inspire_service_link_label": "geo.admin.ch",
"invalid_email": "Invalid email",
"invalid_file": "Invalid file",
"invalid_kml_gpx_file_error": "Invalid file, only KML or GPX file are supported",
"invalid_import_file_error": "Invalid file, only KML, KMZ, GPX or GeoTIFF file are supported",
"invalid_url": "URL is not valid.",
"invalid_wms_capabilities": "Invalid WMS Capabilities",
"invalid_wmts_capabilities": "Invalid WMTS Capabilities",
Expand All @@ -325,7 +326,6 @@
"kgs_service_link_label": "Protection of Cultural Property",
"kml": "KML",
"kml_gpx_file_empty": "KML/GPX file is empty",
"kml_gpx_file_out_of_bounds": "File content is out of projection bounds",
"kml_icon_url_cors_issue": "The KML \"{layerName}\" contains icon(s) with URL {url} that doesn't support CORS! Please contact the administrator of this URL to add support for CORS.",
"kml_no_text_elements": "Information: The labels won't be saved",
"kml_style_default": "default",
Expand Down
10 changes: 5 additions & 5 deletions src/modules/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"drawing_attached": "Dessin ajouté en pièce jointe",
"drawing_too_large": "Ton dessin est trop grand, enlève quelques éléments",
"drop_invalid_url": "URL non valide.",
"drop_me_here": "Déplacer le fichier KML ici",
"drop_me_here": "Déposer le fichier ici (KML, KMZ, GPX, GeoTIFF)",
"duplicate_layer": "Duplication de carte",
"east": "Est",
"ech": "Géocatalogue",
Expand Down Expand Up @@ -293,22 +293,23 @@
"import": "Importer",
"import_file": "Importer un fichier",
"import_file_succeeded": "Succès",
"import_file_tooltip": "Importer un fichier KML ou GPX externe",
"import_file_url_placeholder": "GPX KML URL",
"import_file_tooltip": "Importer un fichier KML, KMZ, GPX ou GeoTIFF externe",
"import_file_url_placeholder": "GPX KML KMZ GeoTIFF URL",
"import_kml": "KML import",
"import_maps": "Importer des cartes",
"import_maps_tooltip": "Importer des données WMTS WMS externes",
"import_maps_url_placeholder": "WMTS WMS URL",
"import_online_placeholder": "WMTS WMS GPX KML URL",
"import_tooltip": "Importer des données WMTS WMS GPX KML externes",
"import_wms": "WMS import",
"imported_file_out_of_bounds": "Le contenu du fichier est hors des limites de projection",
"inform_draw_rectangl_ctrl": "Dessiner un rectangle sur la carte avec la touche CTRL (cmd on Mac) enfoncée pour choisir la zone de sélection.",
"inspire": "INSPIRE",
"inspire_service_link_href": "https://www.geo.admin.ch/fr/home.html",
"inspire_service_link_label": "geo.admin.ch",
"invalid_email": "e-mail invalide",
"invalid_file": "Fichier invalide, seuls les fichiers KML ou GPX sont pris en charge",
"invalid_kml_gpx_file_error": "Fichier invalide, seuls les fichiers KML ou GPX sont pris en charge",
"invalid_import_file_error": "Fichier invalide, seuls les fichiers KML, KMZ, GPX ou GeoTIFF sont pris en charge",
"invalid_url": "URL non valide.",
"invalid_wms_capabilities": "Données WMS Capabilities invalides",
"invalid_wmts_capabilities": "Données WMTS Capabilities invalides",
Expand All @@ -325,7 +326,6 @@
"kgs_service_link_label": "Protection des biens culturels",
"kml": "KML",
"kml_gpx_file_empty": "Le fichier KML/GPX est vide",
"kml_gpx_file_out_of_bounds": "Le contenu du fichier est hors des limites de projection",
"kml_icon_url_cors_issue": "Le KML « {layerName} » contient des icônes avec l'URL {url} qui ne supporte pas CORS ! Veuillez contacter l'administrateur de cette URL pour avoir du support de CORS.",
"kml_no_text_elements": "Information: Les étiquettes ne seront pas sauvées",
"kml_style_default": "défaut",
Expand Down
10 changes: 5 additions & 5 deletions src/modules/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"drawing_attached": "Disegno aggiunto come allegato",
"drawing_too_large": "Il suo disegno è troppo grande, rimuova alcuni elementi",
"drop_invalid_url": "URL non valido",
"drop_me_here": "Spostare file KML qui",
"drop_me_here": "Lasciare qui il file (KML, KMZ, GPX, GeoTIFF)",
"duplicate_layer": "Mappa duplicata",
"east": "Est",
"ech": "Geocatalogo",
Expand Down Expand Up @@ -293,22 +293,23 @@
"import": "Importare",
"import_file": "Importare file",
"import_file_succeeded": "Successo",
"import_file_tooltip": "Importa un file KML o GPX esterno",
"import_file_url_placeholder": "GPX KML URL",
"import_file_tooltip": "Importa un file KML, KMZ, GPX o GeoTIFF esterno",
"import_file_url_placeholder": "GPX KML KMZ GeoTIFF URL",
"import_kml": "Importare KML",
"import_maps": "Importa mappe",
"import_maps_tooltip": "Importare dati WMTS WMS esterni",
"import_maps_url_placeholder": "WMTS WMS URL",
"import_online_placeholder": "WMTS WMS GPX KML URL",
"import_tooltip": "Importare dati WMTS WMS GPX KML esterni",
"import_wms": "Importare WMS",
"imported_file_out_of_bounds": "Il contenuto del file è fuori dai limiti della proiezione",
"inform_draw_rectangl_ctrl": "Disegnare un rettangolo sulla mappa tenendo premuto il tasto CTRL (cmd per Mac) per selezionare più oggetti contemporaneamente",
"inspire": "INSPIRE",
"inspire_service_link_href": "https://www.geo.admin.ch/it/home.html",
"inspire_service_link_label": "geo.admin.ch",
"invalid_email": "e-mail non valido",
"invalid_file": "file non valido",
"invalid_kml_gpx_file_error": "File non valido, sono supportati solo file KML o GPX",
"invalid_import_file_error": "File non valido, sono supportati solo file KML, KMZ, GPX o GeoTIFF",
"invalid_url": "URL non valido",
"invalid_wms_capabilities": "Dati WMS Capabilities non validi",
"invalid_wmts_capabilities": "Dati WMTS Capabilities non validi",
Expand All @@ -325,7 +326,6 @@
"kgs_service_link_label": "Protezione dei beni culturali",
"kml": "KML",
"kml_gpx_file_empty": "Il file KML/GPX è vuoto",
"kml_gpx_file_out_of_bounds": "Il contenuto del file è fuori dai limiti della proiezione",
"kml_icon_url_cors_issue": "Il KML “{layerName}” contiene icone con l'URL {url} che non supporta CORS! Contattare l'amministratore di questo URL per aggiungere il supporto per CORS.",
"kml_no_text_elements": "Informazione: le etichette non saranno salvate",
"kml_style_default": "predefinito",
Expand Down
10 changes: 5 additions & 5 deletions src/modules/i18n/locales/rm.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@
"drawing_attached": "Dissegn è agiuntà sco agiunta.",
"drawing_too_large": "Tes dissegn è memia grond, stizza intgins detagls",
"drop_invalid_url": "URL è nunvalid",
"drop_me_here": "Trair vi la datoteca KML nà qua",
"drop_me_here": "Dar giu la datoteca (KML, KMZ, GPX, GeoTIFF)",
"duplicate_layer": "Mapa duplicada",
"east": "ost",
"ech": "Catalog da geodatas",
Expand Down Expand Up @@ -291,22 +291,23 @@
"import": "Importar",
"import_file": "Importar ina datoteca",
"import_file_succeeded": "Success",
"import_file_tooltip": "Importar ina datoteca esterna KML u GPX",
"import_file_url_placeholder": "GPX KML URL",
"import_file_tooltip": "Importar ina datoteca esterna KML, KMZ, GPX u GeoTIFF",
"import_file_url_placeholder": "GPX KML KMZ GeoTIFF URL",
"import_kml": "Importar KML",
"import_maps": "Importar charta",
"import_maps_tooltip": "Agiuntar in unitad da datas WMTS WMS externa",
"import_maps_url_placeholder": "WMTS WMS URL",
"import_online_placeholder": "WMTS WMS GPX KML URL",
"import_tooltip": "Agiuntar in unitad da datas WMTS WMS GPX KML externa",
"import_wms": "Importar WMS",
"imported_file_out_of_bounds": "Il contetn dal file è en furma da las limitas da projezziun",
"inform_draw_rectangl_ctrl": "Per selecziunar plirs objects en ina: smatgai CTRL e faschai in rectangul sin la charta.",
"inspire": "INSPIRE",
"inspire_service_link_href": "https://www.geo.admin.ch/rm/home.html",
"inspire_service_link_label": "geo.admin.ch",
"invalid_email": "ungültige E-Mail",
"invalid_file": "Datotecadad nun vala",
"invalid_kml_gpx_file_error": "Datotecadad nun vala, èn ancum suttatschadas ils files KML u GPX.",
"invalid_import_file_error": "Datotecadad nun vala, èn ancum suttatschadas ils files KML, KMZ, GPX u GeoTIFF.",
"invalid_url": "URL è nunvalid",
"invalid_wms_capabilities": "Dadis WMS Capabilitiesinvalid",
"invalid_wmts_capabilities": "Dadis WMTS Capabilitiesinvalid",
Expand All @@ -323,7 +324,6 @@
"kgs_service_link_label": "Protecziun dals bains culturals",
"kml": "KML",
"kml_gpx_file_empty": "La datoteca KML/GPX è vegnida empruva",
"kml_gpx_file_out_of_bounds": "Il contetn dal file è en furma da las limitas da projezziun",
"kml_icon_url_cors_issue": "Il KML \"{layerName}\" cuntegna icona(s) cun URL {url} che na po nagin sustegnair CORS! As drizzai p.pl. a l'administratura da quella URL per agiuntar il sustegn dal CORS.",
"kml_no_text_elements": " Infurmaziun: ils labels na vegnan betg memorisads",
"kml_style_default": "Standard",
Expand Down
53 changes: 53 additions & 0 deletions src/modules/map/components/openlayers/OpenLayersGeoTIFF.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script setup>
import WebGLTileLayer from 'ol/layer/WebGLTile'
import GeoTIFFSource from 'ol/source/GeoTIFF'
import { computed, inject, toRefs, watch } from 'vue'
import GeoTIFFLayer from '@/api/layers/GeoTIFFLayer.class'
import useAddLayerToMap from '@/modules/map/components/openlayers/utils/useAddLayerToMap.composable'
const props = defineProps({
geotiffConfig: {
type: GeoTIFFLayer,
required: true,
},
parentLayerOpacity: {
type: Number,
default: null,
},
zIndex: {
type: Number,
default: -1,
},
})
const { geotiffConfig, parentLayerOpacity, zIndex } = toRefs(props)
const olMap = inject('olMap')
const source = computed(() => {
if (geotiffConfig.value.isLocalFile) {
return { blob: geotiffConfig.value.data }
}
return { url: geotiffConfig.value.fileSource }
})
const opacity = computed(() => parentLayerOpacity.value ?? geotiffConfig.value.opacity)
const layer = new WebGLTileLayer({
source: createLayerSource(),
opacity: opacity.value,
})
useAddLayerToMap(layer, olMap, zIndex)
watch(opacity, (newOpacity) => layer.setOpacity(newOpacity))
watch(source, () => layer.setSource(createLayerSource()))
function createLayerSource() {
return new GeoTIFFSource({
convertToRGB: 'auto',
sources: [source.value],
})
}
</script>
<template>
<slot />
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AbstractLayer from '@/api/layers/AbstractLayer.class'
import LayerTypes from '@/api/layers/LayerTypes.enum'
import OpenLayersExternalWMTSLayer from '@/modules/map/components/openlayers/OpenLayersExternalWMTSLayer.vue'
import OpenLayersGeoJSONLayer from '@/modules/map/components/openlayers/OpenLayersGeoJSONLayer.vue'
import OpenLayersGeoTIFF from '@/modules/map/components/openlayers/OpenLayersGeoTIFF.vue'
import OpenLayersGPXLayer from '@/modules/map/components/openlayers/OpenLayersGPXLayer.vue'
import OpenLayersKMLLayer from '@/modules/map/components/openlayers/OpenLayersKMLLayer.vue'
import OpenLayersVectorLayer from '@/modules/map/components/openlayers/OpenLayersVectorLayer.vue'
Expand Down Expand Up @@ -129,6 +130,12 @@ function shouldAggregateSubLayerBeVisible(subLayer) {
:parent-layer-opacity="parentLayerOpacity"
:z-index="zIndex"
/>
<OpenLayersGeoTIFF
v-if="layerConfig.type === LayerTypes.GEOTIFF"
:geotiff-config="layerConfig"
:parent-layer-opacity="parentLayerOpacity"
:z-index="zIndex"
/>
<slot />
</div>
</template>
Loading

0 comments on commit 874029c

Please sign in to comment.