Public
Edited
Jul 12, 2022
Importers
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rawCountries = {
switch (basemapRes) {
case '110m': return FileAttachment("ne_110m_admin_0_countries.json").json()
case '50m' : return FileAttachment("ne_50m_admin_0_countries.json").json()
case '10m' : return FileAttachment("ne_10m_admin_0_countries@2.json").json()
}
}
Insert cell
countries = topojson.feature(rawCountries, rawCountries.objects[`countries_${basemapRes}`])
Insert cell
Insert cell
Insert cell
Insert cell
// Polygones du Maroc et du Sahara occidental
MA_SO = countries.features.filter( d => d.properties.ADM0_A3 == "MAR" ||
d.properties.ADM0_A3 == "SAH")
Insert cell
// 1.
MA_SO_border = turf.lineString([[-14, 27.66], [-7, 27.66]])
Insert cell
MA_SO_lines = {
// 2. Fusion des 2 polygones
const union = turf.union(MA_SO[0], MA_SO[1])

// 3. Conversion en ligne
const line = turf.polygonToLine(union)

// 4. Découpe selon la nouvelle frontière
const lineSplit = turf.lineSplit(line, MA_SO_border)

return lineSplit.features
}
Insert cell
MAR = {
const poly = turf.lineToPolygon(MA_SO_lines[1])
poly.properties = MA_SO.filter( d => d.properties.ADM0_A3 == "MAR")[0].properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
SAH = {
const SAH_line = ({
type: "LineString",
coordinates: MA_SO_lines[0].geometry.coordinates
.concat(MA_SO_lines[2].geometry.coordinates)
})
const poly = turf.lineToPolygon(SAH_line)
poly.properties = MA_SO.filter( d => d.properties.ADM0_A3 == "SAH")[0].properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
Insert cell
// Polygones de la Somalie et du Somaliland
SOM_SOL = countries.features.filter( d => d.properties.ADM0_A3 == "SOM" || d.properties.ADM0_A3 == "SOL")
Insert cell
SOM = {
const poly = turf.union(SOM_SOL[0], SOM_SOL[1]) // fusion des 2 polygones
poly.properties = SOM_SOL.filter( d => d.properties.ADM0_A3 == "SOM")[0].properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
Insert cell
CYP_CYN = countries.features.filter( d =>
d.properties.ADM0_A3 == "CYP" || // Chypre
d.properties.ADM0_A3 == "CYN" || // Chypre du Nord
// seulement version 10m
d.properties.ADM0_A3 == "CNM" || // No Mans Area
d.properties.ADM0_A3 == "ESB" || // Dhekelia, Eastern Sovereign Base Area
d.properties.ADM0_A3 == "WSB") // Akrotiri, Western Sovereign Base Area
Insert cell
CYP = {
// Union successive des polygones de Chypre car turf.union ne se fait que 2 par 2
const poly = CYP_CYN.reduce( (previousCall, current) => turf.union(previousCall, current))
poly.properties = CYP_CYN.filter( d => d.properties.ADM0_A3 == "CYP")[0].properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
Insert cell
UKR_raw = countries.features.filter( d => d.properties.ADM0_A3 == "UKR")[0]
Insert cell
// MultiPolygone de la Russie
RUS_mp = countries.features.filter( d => d.properties.ADM0_A3 == "RUS")[0]
Insert cell
// 1. Polygones de la Russie
RUS_p = turf.flatten(RUS_mp.geometry)
Insert cell
crimea = {
// 2. Centroïde de la Crimée
const centroid = turf.point([34.03,45.20])
// Test l'appartenance du centroïde de la Crimée à chacun des polygones de la Russie
const isInCrimea = RUS_p.features.map(d => turf.booleanPointInPolygon(centroid, d)) // [false,true,false...]

// Index du polygone de la Crimée
const index = isInCrimea.findIndex(d => d == true)

// 3. Extrait le polygone de la Crimée dans ceux de la Russie
const poly = RUS_p.features[index]
return poly
}
Insert cell
// 4.
UKR = {
const poly = turf.union(UKR_raw, crimea)
poly.properties = UKR_raw.properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
// 5.
RUS = {
let poly = turf.difference(RUS_mp, crimea)
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
Insert cell
// Polygones du glacier du Siachen et de l'Inde
KAS_IND = {
if (basemapRes == '110m') return null
const poly = countries.features.filter( d => d.properties.ADM0_A3 == "KAS" || d.properties.ADM0_A3 == "IND")

return poly
}
Insert cell
IND = {
if (KAS_IND == null) return null
let poly = turf.union(KAS_IND[0], KAS_IND[1]) // fusion des 2 polygones
poly.properties = KAS_IND.filter( d => d.properties.ADM0_A3 == "IND")[0].properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
Insert cell
// Polygones de Baïkonour et du Kazakhstan
KAB_KAZ = {
if (basemapRes != '10m') return null
const poly = countries.features.filter( d => d.properties.ADM0_A3 == "KAB" || d.properties.ADM0_A3 == "KAZ")

return poly
}
Insert cell
KAZ = {
if (KAB_KAZ == null) return null
let poly = turf.union(KAB_KAZ[0], KAB_KAZ[1]) // fusion des 2 polygones
poly.properties = KAB_KAZ.filter( d => d.properties.ADM0_A3 == "KAZ")[0].properties
return turf.rewind(poly, {reverse: true})
}
Insert cell
Insert cell
// MultiPolygone de la France
FRA_mp = countries.features.filter( d => d.properties.ADM0_A3 == "FRA")[0]
Insert cell
FRA_DROM_bbox = ([
// Guadeloupe, https://www.iso.org/obp/ui/fr/#iso:code:3166:GP
{id: 'GLP', bbox: [-61.9577,15.7126,-60.8795,16.5815], properties: {ADM0_A3: 'GLP', NAME_FR: 'Guadeloupe'}},
// Martinique, https://www.iso.org/obp/ui/fr/#iso:code:3166:MQ
{id: 'MTQ', bbox: [-61.3635,14.2674,-60.6417,14.9838], properties: {ADM0_A3: 'MTQ', NAME_FR: 'Martinique'}},
// Guyane, https://www.iso.org/obp/ui/fr/#iso:code:3166:GF
{id: 'GUF', bbox: [-54.956,1.665,-51.056,6.297], properties: {ADM0_A3: 'GUF', NAME_FR: 'Guyane française'}},
// Réunion, https://www.iso.org/obp/ui/fr/#iso:code:3166:RE
{id: 'REU', bbox: [55.1245,-21.4749,55.9962,-20.7611], properties: {ADM0_A3: 'REU', NAME_FR: 'Réunion'}},
// Mayotte, https://www.iso.org/obp/ui/fr/#iso:code:3166:YT
{id: 'MYT', bbox: [44.9622,-13.0513,45.3488,-12.6003], properties: {ADM0_A3: 'MYT', NAME_FR: 'Mayotte'}},
// France métropolitaine
{id: 'FRA', bbox: [-5.995,40.209,10.815,52.038], properties: null}
])
Insert cell
FRA_DROM = FRA_DROM_bbox
.map(d => {
if (basemapRes == '110m' && d.id != 'GUF' && d.id != 'FRA') return null
const poly = turf.bboxClip(FRA_mp, d.bbox)
d.id == 'FRA' ? poly.properties = FRA_mp.properties : poly.properties = d.properties
return poly
})
.filter(d => d != null)
Insert cell
Insert cell
track = {
let remove = [], add = []

remove.push(MA_SO, SOM_SOL, CYP_CYN, RUS_mp, UKR_raw, FRA_mp)
add.push(MAR, SAH, SOM, CYP, UKR, RUS, FRA_DROM)
if (basemapRes != '110m') {
remove.push(KAS_IND)
add.push(IND)
}

if (basemapRes == '10m') {
remove.push(KAB_KAZ)
add.push(KAZ)
}

return {remove: remove.flat(), add: add.flat()}
}
Insert cell
countries_ok = {
const data = {...countries}
return turf.featureCollection(
// supprimer les pays qui ont été modifiés
_.without(data.features, ...track.remove)
// ajouter les nouveaux pays
.concat(track.add)
)
}
Insert cell
Insert cell
countries_topojson = topojson.topology({[`countries_${basemapRes}`]: countries_ok})
Insert cell
Insert cell
Insert cell
countries_ok_light = {
let data = countries_ok
data.features.forEach( d => {
// Ajoute deux propriétés 'id' et 'name'
d.properties['id'] = d.properties.ADM0_A3
d.properties['name'] = d.properties.NAME_FR
// Renomme la Chine dans sa version courte
if (d.properties['id'] == 'CHN') d.properties['name'] = "Chine"
// Supprime toutes les autres propriétés
const keep = _.pick(d.properties, ['id', 'name'])
d.properties = keep
})
return data
}
Insert cell
// Reconversion en topojson
countries_topojson_light = topojson.topology({[`countries_${basemapRes}`]: countries_ok_light})
Insert cell
Insert cell
countries_simplified = {
// s = niveau de simplification et q = niveau de 'quantization'
let s = 1, q = 1e4 // 110m
if (basemapRes == '50m') { s = 0.6 } // 50m
if (basemapRes == '10m') { s = 0.4; q = 1e5 } // 10m

let poly = topojson.presimplify(countries_topojson_light, topojson.sphericalTriangleArea);
let min_weight = topojson.quantile(poly, s);
poly = topojson.simplify(poly, min_weight);
poly = topojson.quantize(poly, q)
return poly;
}
Insert cell
Insert cell
Insert cell
Insert cell
countriesBbox = countries_ok_light.features
.map( d => ({...d.properties,
// ajout de la bbox
bbox: turf.bbox(d).map(d => +d.toFixed(2)),
// ajout du centroïde
centroid: turf.centroid(d).geometry.coordinates.map(d => +d.toFixed(2))
}) )
// tri alphabétique sur le nom des pays
.sort( (a,b) => a.name.localeCompare(b.name) )
Insert cell
Insert cell
Insert cell
addMap = (dimensions, geojson, zoom = false) => {
const path = d3.geoPath(proj.fitSize(dimensions, geojson))
const svg = htl.svg
`<svg width="${dimensions[0]}" height="${dimensions[1]}" style="display: block;">
<g id="zoom">
<path d="${path(geojson)}" fill="#ffa98f" stroke="white" stroke-width="0.3" vector-effect="non-scaling-stroke"></path>
</g>
</svg>`

// Pan & Zoom optionnel
if (zoom) {
const zoom = d3
.zoom()
.scaleExtent([1, 20]) // min, max du zoom
.translateExtent([[0, 0], dimensions]) // bornes extérieures du translate
.on("zoom", ({ transform }) => d3.select(svg).select("g#zoom").attr("transform", transform))
d3.select(svg).call(zoom)
}
return svg
}
Insert cell
Insert cell
proj = d3.geoEqualEarth().rotate([-15,0])
Insert cell
Insert cell
turf = require("@turf/turf@6")
Insert cell
topojson = require('topojson-client', 'topojson-server', 'topojson-simplify')
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more