Système de projection du geotiff :
Les fichiers sources viennent de la conversion des fichiers ASC fournit par l'IGN en geotiff(float32 et données compressées).
Le fond de carte est le fond open streetmap standard, le tout projeté en RGF 93.
Le code utilise OpenLayer en version 7.1 et geotiff.js en dernière version
Le code ne fonctionne que si la projection est la même que les geotiffs, toutefois avec une petite adaptation il est possible de récupérer la donnée d'origine en utilisant proj4.js
Cette config est nécessaire pour la lecture partielle des geotiff. C'est un besoin spécifique à la lecture des geotiff par url
<Directory votre chemin de stockage sur le serveur/range>
AllowOverride None
Order Allow,Deny
Allow from All
<IfModule mod_headers.c>
#nécessaire si le site d'utilisation n'est pas sur le même site
Header set Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers: "Content-Type, Range"
Header add Access-Control-Allow-Methods: "HEAD,GET,POST,OPTIONS"
Header add Access-Control-Allow-Methods: "authorization"
</IfModule>
</Directory>
L'utilisation des rasters tiff est montré dans les exemples d'open layer je n'y reviendrais pas. Toutefois, il faut tenir compte de l'utilisation des méthodes asynchrone pour y arriver et de promise pour simplifier la lecture et l'écriture j'utilise donc une fonction asynchrone pour la génération de la carte
Dans les exemples d'utilisation d'openLayer geoTiff il existe 2 méthodes d'utilisations des fichiers, les deux sont utilisées pour déterminer les différences d'affichages. La différence des méthodes se voit dès l'affichage des données, mais l'importance est ailleurs l'importance, c'est la méthode de lecture du fichier en 1 bloc(blob) ou par morceau(url).
/*** Les Imports ***/
import GeoTIFF from 'ol/source/GeoTIFF';
import Map from 'ol/Map';
import WebGLTileLayer from 'ol/layer/WebGLTile';
import OSM from 'ol/source/OSM';
import TileLayer from 'ol/layer/Tile';
import View from 'ol/View';
import proj4 from 'proj4';
import MousePosition from 'ol/control/MousePosition';
import {register} from 'ol/proj/proj4';
import {createStringXY} from 'ol/coordinate';
import {defaults as defaultControls} from 'ol/control';
import {fromBlob, fromUrl} from "geotiff";
/***la fonction principale***/
/*Préparer l'ensemble en fonction asynchrone pour permettre l'attente des données*/
export let mainPrg = async function (mousePosition,targetMap,realAltitudeLabel,altitudeLabel) {
/***la gestion de la projection locale RGF(93) EPSG:2154***/
proj4.defs("EPSG:2154", "+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000
+ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
register(proj4);
const mousePositionControl = new MousePosition({
coordinateFormat: createStringXY(3),
projection: 'EPSG:2154',
// comment the following two lines to have the mouse position
// be placed within the map.
className: 'custom-mouse-position',
target: document.getElementById('mouse-position'),
});
/* Couche de fond */
let lyr_OSMStandard_0 = new TileLayer({
source: new OSM(),
});
/*Obtention du blob*/
let response = await fetch('https://www.blog-db.fr/images/range/test-sud.tif')
const blob20 = await response.blob()
const sourceSud = new GeoTIFF({
sources: [
{
blob: blob20,
},
],
});
/*Création de la source nord, NB il est possible de récupérer les données par url.
Avec le blob on lit le fichier en une fois et les données seront utilisées par
openlayers et pour l'obtention de l'altitude (valeur obtenue au plus près).
Le chargement des données en 1 seule fois.
Avec l'URL on lit le fichier morceau par morceau d'où l'importance du Range côté serveur.
Le hic c'est que le chargement se fait pour openLayer et pour l'obtention de l\'altitude.
Le chargement des données complètes pourra être faite 2 fois(mais le chargement est limité au besoin).*/
const urlNord = 'https://www.blog-db.fr/images/range/test-nord.tif';
const sourceNord = new GeoTIFF({
sources: [
{
url: urlNord,
},
],
interpolate: false,
sourceOptions: {
allowFullFile: true,
}
});
/*Création des couches à partir des couches*/
let layerSud = new WebGLTileLayer({
source: sourceSud
});
let layerNord = new WebGLTileLayer({
source: sourceNord,
});
/***Création de la vue***/
let tryView = new View({
projection: 'EPSG:2154',
center: [855500, 6520000],
zoom: 15,
});
/***Création de la carte***/
const map = new Map({
controls: defaultControls().extend([mousePositionControl]),
target: targetMap,
layers: [
lyr_OSMStandard_0,
layerNord,
layerSud,
],
view: tryView
});
/***Lecture du Tiff contenu dans le blob***/
const tiffSud = await fromBlob(blob20);
const imageSud = await tiff.getImage();
/***Lecture des informations (métadata)***/
const originSud = image.getOrigin();
const resolutionSud = image.getResolution();
const geoKeysSud = image.getGeoKeys();
const projectionSud = geoKeysSud.ProjectedCSTypeGeoKey+ ' : ' + geoKeysSud.GTCitationGeoKey;
/***Lecture des données de l\'image***/>
const dataSud = await image.readRasters();
const widthSud = await dataSud.width;
/***Calcul de la coordonnée du centre du point haut et gauche du geotiff***/
const originSudRefX = origin[0] + resolution[0] / 2;
const originSudRefY = origin[1] + resolution[1] / 2;
/***données nord***/
const tiffNord = await fromUrl(urlNord);
const imageNord = await tiffNord.getImage();
const originNord = imageNord.getOrigin();
const resolutionNord = imageNord.getResolution();
let geoKeysNord = imageNord.getGeoKeys();
const projectionNord = geoKeysNord.ProjectedCSTypeGeoKey + ' : ' + geoKeysNord.GTCitationGeoKey;
const originNordRefX = originNord[0] + resolutionNord[0] /2;
const originNordRefY = originNord[1] + resolutionNord[1] /2;
/***Création de la gestion au déplacement du curseur***/
map.on('pointermove', async (event) => {
/***Obtention des coordonnées dans le système de projection de la map***/
let x = event.coordinate[0];
let y = event.coordinate[1];
/***Initialisation des valeurs***/
let chooseLayer = layerSud;
projectionLabel.innerHTML = '';
/***Si la couche layerNord est présente sur le pixel***/
if (layerNord.getData(event.pixel) && layerNord.getData(event.pixel)[0]) {
chooseLayer = layerNord;
/***Calcul des coordonnées dans l'image***/
let coordXImage = Math.round((x - originNordRefX) / resolutionNord[0]);
let coordYImage = Math.round((y - originNordRefY) / resolutionNord[1]);
/***Paramétrage du formatage***/
let int4 = new Intl.NumberFormat("fr-FR", {maximumFractionDigits: 2, minimumFractionDigits: 2});
let options;
projectionLabel.innerHTML = projectionNord;
/***Lecture des données nécessaires***/
options = {
window: [coordXImage, coordYImage, coordXImage+1, coordYImage+1],
width:1,
height:1
};
let dataPixel = await imageNord.readRasters(options);
/***Affichage de la donnée du pixel***/
realAltitudeLabel.innerHTML = int4.format(dataPixel[0][0]).toString();
/***Si la couche layerSud est présente sur le pixel***/
} else if (layerSud.getData(event.pixel) && layerSud.getData(event.pixel)[0]) {
let coordXImage = Math.round((x - originSudRefX) / resolution[0]);
let coordYImage = Math.round((y - originSudRefY) / resolution[1]);
projectionLabel.innerHTML = projectionSud;
let int4 = new Intl.NumberFormat("fr-FR", {maximumFractionDigits: 2, minimumFractionDigits: 2});
/*** Affichage de la donnée du pixel ***/
realAltitudeLabel.innerHTML = int4.format(data[0][widthSud * coordYImage + coordXImage]).toString();
/*** Si aucune couche est présente sur le pixel ***/
} else {
realAltitudeLabel.innerHTML = 'no data';
altitudeLabel.innerHTML = 'no data';
}
/*** Données avec les valeurs des pixels ***/
if (chooseLayer.getData(event.pixel)) {
let maximum = chooseLayer.getSource().metadata_[0][0].STATISTICS_MAXIMUM;
let minimum = chooseLayer.getSource().metadata_[0][0].STATISTICS_MINIMUM;
let rapport = (maximum - minimum) / 255;
/*** Utilisation des données du pixel ***/
let altitudeCalc = rapport * chooseLayer.getData(event.pixel)[0] + (1.0 * minimum);
altitudeLabel.innerHTML = altitudeCalc.toFixed(2).toString();
}
});
}
let altitudeLabel = document.getElementById('altitude-label');
let realAltitudeLabel = document.getElementById('altitude-tif-label');
let mousePosition = document.getElementById('mouse-position');
let targetMap = 'map';
let projectionLabel = document.getElementById('projection-label')
mainPrg(mousePosition,targetMap,realAltitudeLabel,altitudeLabel,projectionLabel).then((test=>{console.log('loaded')}));
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Cloud Optimized GeoTIFF (COG)</title>
<link rel="stylesheet" href="cog_fichiers/ol.css">
<style>
.map {
width: 80%;
height: 60vh;
}
</style>
</head>
<body>
<h4></h4>
<div id="map" class="map"></div>
<div id="mouse-position"></div>
<div id="projection-label"></div>
<div id="altitude-label"></div>
<div id="altitude-tif-label"></div>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="https://unpkg.com/elm-pep@1.0.6/dist/elm-pep.js"></script>
<script type="module" src="cog_fichiers/final.js"></script>
</body>
</html>
{ "name": "cog", "dependencies": { "geotiff": "^2.0.5", "ol": "7.1.0", "proj4": "^2.8.0" }, "devDependencies": { "@babel/core": "latest", "vite": "^3.0.3" }, "scripts": { "start": "vite", "build": "vite build" } }