// A FAIRE
// idée garder juste les colonnes du pop up puis fabriquer une colonne avec un json
// ce json mutable sera ensuite exploité dans la partie texte !
myMap = {
const entete = document.getElementById('quarto-header');
const headerHeight = entete ? entete.getBoundingClientRect().height : 0;
const availableHeight = window.innerHeight - headerHeight - 120; // forfait de 40px pour prendre de la marge
const container = html`<div style="width: 100%; height: ${availableHeight}px; margin: 0; padding: 0;">`;
// on définit le container de la carte et on l'attend , on mettant fill: false la card prend la taille de la map et en réglant le height en vh en tatonnant on trouve la bonne proportion
//const container = html`<div style="width:100%; height:55vh; margin:0; padding:0;">`;
yield container; // comme un return mais ça continue le code qui suit
// add the PMTiles plugin to the maplibregl global.
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol('pmtiles', (request) => {
return new Promise((resolve, reject) => {
const callback = (err, data) => {
if (err) {
reject(err);
} else {
resolve({data});
}
};
protocol.tile(request, callback);
});
});
//const PMTILES_URL_B1 = 'https://guillaumechretiencerema.github.io/mydatas/recoquartiers/bat_48_58.pmtiles?raw=true';
//const PMTILES_URL_B1 = 'http://162.19.124.68/recoquartiers/0-pmtiles/bat_48_58.pmtiles';
const PMTILES_URL_B1 = `${LAYERS_PATH}/batiments_groupes_cpl_detail.pmtiles`;
const b1 = new pmtiles.PMTiles(PMTILES_URL_B1);
protocol.add(b1);
const PMTILES_URL_C =`${LAYERS_PATH}/communes.pmtiles`;
const c = new pmtiles.PMTiles(PMTILES_URL_C);
protocol.add(c);
const PMTILES_URL_TUPS =`${LAYERS_PATH}/tache_tups_cat_regroupee_descohre.pmtiles`;
const tups = new pmtiles.PMTiles(PMTILES_URL_TUPS);
protocol.add(tups);
const PMTILES_URL_ENSBH =`${LAYERS_PATH}/ens_bati_meme_categorie.pmtiles`;
const ensbh = new pmtiles.PMTiles(PMTILES_URL_ENSBH);
protocol.add(ensbh);
const sourceOrtho = {style:'normal', format:'image/jpeg',layer:'HR.ORTHOIMAGERY.ORTHOPHOTOS'};
const sourcePlanIGN = {style:'normal', format:'image/png',layer:'GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2'};
const sourceHillShade = {style:'estompage_grayscale',format:'image/png',layer:'ELEVATION.ELEVATIONGRIDCOVERAGE.SHADOW'};
const map = new maplibregl.Map({
container,
center: [1.0920, 49.4381],
zoom: 9,
pitch: 0,
maxPitch: 75,
style: {
version: 8,
// glyph nécessaires pour les text dans les symbol...
glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf",
sources: {
'raster-planign': {
type: 'raster',
//'tiles': ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
tiles: [
`https://data.geopf.fr/wmts?SERVICE=WMTS&style=${sourcePlanIGN.style}&VERSION=1.0.0&REQUEST=GetTile&format=${sourcePlanIGN.format}&layer=${sourcePlanIGN.layer}&tilematrixset=PM&TileMatrix={z}&TileCol={x}&TileRow={y}`
],
tileSize: 256,
attribution: '© <a href="https://www.ign.fr/">IGN</a>',
minzoom: 0,
maxzoom: 22
},
'raster-ortho': {
type: 'raster',
//'tiles': ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
tiles: [
`https://data.geopf.fr/wmts?SERVICE=WMTS&style=${sourceOrtho.style}&VERSION=1.0.0&REQUEST=GetTile&format=${sourceOrtho.format}&layer=${sourceOrtho.layer}&tilematrixset=PM&TileMatrix={z}&TileCol={x}&TileRow={y}`
],
tileSize: 256,
attribution: '© <a href="https://www.ign.fr/">IGN</a>',
minzoom: 0,
maxzoom: 22
},
'hillshadeSource': {
type: 'raster',
//'tiles': ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
tiles: [
`https://data.geopf.fr/wmts?SERVICE=WMTS&style=${sourceHillShade.style}&VERSION=1.0.0&REQUEST=GetTile&format=${sourceHillShade.format}&layer=${sourceHillShade.layer}&tilematrixset=PM&TileMatrix={z}&TileCol={x}&TileRow={y}`
],
tileSize: 256,
attribution: '© <a href="https://www.ign.fr/">IGN</a>',
minzoom: 0,
maxzoom: 22
},
'source_c': {
type: 'vector',
url: `pmtiles://${PMTILES_URL_C}`
},
'source_b1': {
type: 'vector',
url: `pmtiles://${PMTILES_URL_B1}`
},
'source_tups': {
type: 'vector',
url: `pmtiles://${PMTILES_URL_TUPS}`
},
'source_ensbh': {
type: 'vector',
url: `pmtiles://${PMTILES_URL_ENSBH}`
}
},
layers: [
// PLAN IGN
{
id: 'raster-planign',
type: 'raster',
source: 'raster-planign',
layout: {
// Make the layer visible by default.
visibility: 'visible' //ou none
},
},
// ORTHOPHOTO
{
id: 'raster-ortho',
type: 'raster',
source: 'raster-ortho',
layout: {
// Make the layer visible by default.
visibility: 'none' //ou none
},
},
// OMBRES
{
id: 'hillshadeSource',
type: 'raster',
source: 'hillshadeSource',
layout: {
// Make the layer visible by default.
visibility: 'visible' //ou none
},
},
// COMMUNES
{
id: "communes",
source: "source_c",
'source-layer': "communes",
type: "line",
paint: {
'line-color': 'white',
'line-width': 2
}
},
// ASTUCE POUR LE BATIMENT SELECTIONNE
{
id: "selectedB",
source: "source_b1",
'source-layer': 'batiments_groupes_cpl_detail',
/*type: "line",
layout: {
"line-join": "round",
"line-cap": "round"
},
paint: {
"line-color": "yellow",
"line-width": 8
},*/
type: "fill-extrusion", // Changer le type de couche en "fill-extrusion"
paint: {
"fill-extrusion-color": "yellow", // Couleur de remplissage extrudée
"fill-extrusion-height": ["get", "bdtopo_bat_hauteur_mean"], // Propriété de hauteur à utiliser pour extruder les polygones
"fill-extrusion-base": 0, // Hauteur de base de l'extrusion
"fill-extrusion-opacity": 0.7
},
filter: ["==", "batiment_groupe_id", 'bdnb-bg-1112-ULAV-CYVN'] // Filtre initial vide
},
// TUPS AGREGEES PROPRIO
{
id: 'tups_agreges',
source: 'source_tups',
'source-layer': 'tache_tups_cat_regroupee_descohre',
type: "fill", // Changer le type de couche en "fill-extrusion"
paint: {
"fill-color": [
'match',
/*['get', 'methode'],
// Définissez une couleur par défaut si nécessaire :
'#808080' // Couleur grise par défaut pour les valeurs non spécifiées*/
['get', 'id_group_cat'],
"01-02-03","#B34D42",
"04","#D99058",
"05-06","#4682B4",
"07","#D2AE53",
"08","#A9A9A9",
"09","#D19896",
"10","#89666B",
"11","#996699",
"12","#3C6971",
"13","#9BB4D5",
"14","#96A87E",
"white" // couleur si ça match pas
],
"fill-opacity": 0.7
}
},
// BATIMENTS GROUPE DETAIL AVEC COULEUR SELON PROPRIO
{
id: 'batiments_groupes_cpl_detail',
source: 'source_b1',
'source-layer': 'batiments_groupes_cpl_detail',
type: "fill-extrusion", // Changer le type de couche en "fill-extrusion"
paint: {
//"fill-extrusion-color": "purple", // Couleur de remplissage extrudée
"fill-extrusion-color": [
'match',
['get', 'id_cat_pat'],
"01","#795548",
"02","#B34D42",
"03","#DEB887",
"04","#D99058",
"05","#6b8e23",
"06","#4682B4",
"07","#D2AE53",
"08","#A9A9A9",
"09","#D19896",
"10","#89666B",
"11","#996699",
"12","#3C6971",
"13","#9BB4D5",
"14","#96A87E",
"white" // couleur si ça match pas
],
"fill-extrusion-height": ["get", "bdtopo_bat_hauteur_mean"], // Propriété de hauteur à utiliser pour extruder les polygones
"fill-extrusion-base": 0, // Hauteur de base de l'extrusion
"fill-extrusion-opacity": 1
}
},
// ENS BATI MEME CAT
{
id: "ens_bati_homo",
source: "source_ensbh",
'source-layer': "ens_bati_meme_categorie",
type: "line",
paint: {
'line-color': 'yellow',
'line-width': 1
}
},
]
}
});
//------------------------------------------------------------------------------------------------
map.addControl(new maplibregl.NavigationControl(
{
visualizePitch: true,
//showZoom: true,
//showCompass: true
}),
'top-left'
);
map.addControl(new maplibregl.FullscreenControl()), 'top-right';
const scale = new maplibregl.ScaleControl({
maxWidth: 80,
unit: 'metric'
});
map.addControl(scale);
//------------------------------------------------------------------------------------------------
map.on('click', 'batiments_groupes_cpl_detail', (e) => {
// Center the map on the coordinates of any clicked symbol from the 'symbols' layer.
const centroid = turf.centroid(e.features[0].geometry);
map.flyTo({
center: centroid.geometry.coordinates,
zoom: 17,
pitch: 45
});
// POP UP on click
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`Batiment(s groupés) n°${e.features[0].properties.batiment_groupe_id} <br>
Catégorie la plus probable : ${e.features[0].properties.categorie_calculee}
`)
.addTo(map);
mutable myId = e.features[0].properties.idtup;
mutable myTup = e.features[0].properties;
// test highlight NEW !
// Mettre à jour le filtre de la couche de surbrillance pour montrer uniquement l'élément cliqué
map.setFilter('selectedB', ['==', 'batiment_groupe_id', e.features[0].properties.batiment_groupe_id]);
// et l'inverse pour les bâtiments non sélectionné
map.setFilter('batiments_groupes_cpl_detail', ['!=', 'batiment_groupe_id', e.features[0].properties.batiment_groupe_id]);
});
//------------------------------------------------------------------------------------------------
// Change the cursor to a pointer when the it enters a feature in the 'symbols' layer.
map.on('mouseenter', 'batiments_groupes_cpl_detail', () => {
map.getCanvas().style.cursor = 'pointer';
});
// Change it back to a pointer when it leaves.
map.on('mouseleave', 'batiments_groupes_cpl_detail', () => {
map.getCanvas().style.cursor = '';
});
//------------------------------------------------------------------------------------------------
const mySelectCommune = document.getElementById('mySelectCommune');
mySelectCommune.addEventListener('change', () => {
map.fitBounds( JSON.parse(mySelectCommune.value) );
});
//------------------------------------------------------------------------------------------------
// pris sur un exemple, permet de refaire un coup de propre
invalidation.then(() => map.remove());
//------------------------------------------------------------------------------------------------
// TEST API ADRESSE GOUV FR cf header pour les lib css et js et exemple pris sur
// https://github.com/webgeodatavore/photon-geocoder-autocomplete/blob/master/demo/photon-demo-maplibre.js
function AddDomControl(dom) {
this._dom = dom;
}
AddDomControl.prototype.onAdd = function (map) {
this._map = map;
this._container = document.createElement("div");
this._container.className = "maplibregl-ctrl photon-geocoder-autocomplete";
this._container.appendChild(this._dom);
return this._container;
};
AddDomControl.prototype.onRemove = function () {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
};
// Format result in the search input autocomplete
var formatResult = function (feature, el) {
var title = document.createElement("strong");
el.appendChild(title);
var detailsContainer = document.createElement("small");
el.appendChild(detailsContainer);
var details = [];
title.innerHTML = feature.properties.label || feature.properties.name;
var types = {
housenumber: "numéro",
street: "rue",
locality: "lieu-dit",
municipality: "commune",
};
if (types[feature.properties.type]) {
var spanType = document.createElement("span");
spanType.className = "type";
title.appendChild(spanType);
spanType.innerHTML = types[feature.properties.type];
}
if (
feature.properties.city &&
feature.properties.city !== feature.properties.name
) {
details.push(feature.properties.city);
}
if (feature.properties.context) {
details.push(feature.properties.context);
}
detailsContainer.innerHTML = details.join(", ");
};
// Function to show you can do something with the returned elements
function myHandler(featureCollection) {
console.log(featureCollection);
}
// We reused the default function to center and zoom on selected feature.
// You can make your own. For instance, you could center, zoom
// and add a point on the map
function onSelected(feature) {
console.log(feature);
map.setCenter(feature.geometry.coordinates);
map.setZoom(16);
}
// URL for API
var API_URL = "//api-adresse.data.gouv.fr";
// Create search by adresses component
var searchCcontainer = new Photon.Search({
resultsHandler: myHandler,
onSelected: onSelected,
placeholder: "Tapez une adresse",
formatResult: formatResult,
url: API_URL + "/search/?",
feedbackEmail: null,
});
map.addControl(new AddDomControl(searchCcontainer), "bottom-left");
//------------------------------------------------------------------------------------------------
// Ebauche de menu sur mesure
const mapMenuDiv = document.createElement('div');
mapMenuDiv.classList.add('mapFondDePlan');
//mapMenuDiv.innerHTML = '<strong>Fond de plan :</strong>';
map.getContainer().appendChild(mapMenuDiv);
// Ajouter un style pour le z-index
mapMenuDiv.style.position = 'absolute'; // new
mapMenuDiv.style.zIndex = '1000'; // new
/*const customDivHTML = `
<div>
<label for="orthoCheckbox">Orthophoto</label>
<input type="checkbox" id="orthoCheckbox" unchecked>
</div>
`;*/
const customDivHTML = `
<strong>Fond de plan :</strong>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="orthoCheckbox">
<label class="form-check-label" for="orthoCheckbox">Photos aériennes</label>
</div>
`;
mapMenuDiv.innerHTML += customDivHTML;
// doc à creuser
// https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/
document.getElementById('orthoCheckbox').addEventListener('change', function (e) {
e.preventDefault();
e.stopPropagation();
const valeurCheckbox = document.getElementById('orthoCheckbox').checked;
if (valeurCheckbox) {
map.setLayoutProperty('raster-ortho', 'visibility', 'visible');
map.setLayoutProperty('raster-planign', 'visibility', 'none');
} else {
map.setLayoutProperty('raster-ortho', 'visibility', 'none');
map.setLayoutProperty('raster-planign', 'visibility', 'visible');
}
});
// Brouillon pour ajouter mon menu layer !
const mapLayersDiv = document.createElement('div');
mapLayersDiv.classList.add('mapLayers');
//mapMenuDiv.innerHTML = '<strong>Fond de plan :</strong>';
map.getContainer().appendChild(mapLayersDiv);
// Ajouter un style pour le z-index
mapLayersDiv.style.position = 'absolute'; // new
mapLayersDiv.style.zIndex = '1000'; // new
const customDivHTML_2 = `
<button id="toggleMapMenuButton" class="btn btn-light">
<img src="images/icons/layers-half.svg" alt="Icône SVG" width="25" height="25">
</button>
<div id="layersList" ></div>
`
mapLayersDiv.innerHTML += customDivHTML_2;
const toggleableLayerIds= [
"communes",
"batiments_groupes_cpl_detail",
"tups_agreges",
"ens_bati_homo"
];
const aliasLayerIds = {
"communes":"Communes",
"batiments_groupes_cpl_detail":"Bâtiments",
"tups_agreges":"Parcelles",
"ens_bati_homo":"Ensembles bâtis homogènes"
};
// https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/
// Set up the corresponding toggle button for each layer.
for (const id of toggleableLayerIds) {
// Skip layers that already have a button set up.
if (document.getElementById(id)) {
continue;
}
// Create a link.
const link = document.createElement('a');
link.id = id;
link.href = '#';
//link.textContent = id;
link.textContent = aliasLayerIds[id] || id;
link.className = 'active';
// Show or hide layer when the toggle is clicked.
link.onclick = function (e) {
const clickedLayer = this.id;
e.preventDefault();
e.stopPropagation();
const visibility = map.getLayoutProperty(
clickedLayer,
'visibility'
) || 'visible';
// NB : je rajoute ou visible car la première c'est undefined ? ? ?
// Toggle layer visibility by changing the layout object's visibility property.
if (visibility === 'visible') {
map.setLayoutProperty(clickedLayer, 'visibility', 'none');
this.className = '';
} else {
this.className = 'active';
map.setLayoutProperty(
clickedLayer,
'visibility',
'visible'
);
}
};
const layers = document.getElementById('layersList');
layers.appendChild(link);
}
}Limite : logement dans lieu de culte : bâtiment forme complexe qui renvoie facilement vers villa néonormande alors que non. Pareil pour les logements de fonction dans les usines ou les gros bâtiments tertiaires (écoles, etc.)
Incliner et faire tourner la carte
Maintenez enfoncé le bouton droit de la souris et déplacez la pour incliner le point de vue ou faire tourner la carte.
Sélectionner un bâtiment (ou un ensemble groupés)
Cliquez sur un bâtiment (bouton gauche de la souris) pour en visualiser les informations. Lorsque les informations ne sont pas disponible pour un seul bâtiment, c’est un ensemble de bâtiments qui sera sélectionné.
locale = d3.formatLocale({
decimal: ",", // Virgule pour les décimales
thousands: " ", // Espace pour le séparateur de milliers
grouping: [3], // Groupement par 3 chiffres nombre)
});
formatter = locale.format(",.0f")