geopandas/xyzservices

IGN (cx.providers.GeoportailFrance.orthos) migrated their services (wxs.ign.fr to data.geopf.fr)

salahelfarissi opened this issue ยท 7 comments

IGN just changed their routing this month (March 2024).
https://geoservices.ign.fr/services-geoplateforme-diffusion

try:
    cx.add_basemap(
        m.ax,
        source=cx.providers.GeoportailFrance.orthos,
        attribution=True,
        crs=Maps.CRS.GOOGLE_MERCATOR,
        zoom=zoom_level,
    )
except HTTPError as e:
    logger.error("Failed to load basemap: %s", e)
    logger.warning("Continuing without basemap.")

Thanks for the report! It seems that our automatic tooling actually picked it up in f31e557. I will cut a release of xyzservices tomorrow to get it out.

I was actually wrong, we the tooling needs to be migrated.

@HaudinFlorence would you be able to look at it? You did the original implementation.

I can work on it if it suits you.
I already work on a web platform that generates reports and we use their services.

image
image

@salahelfarissi That would be very welcome. The code fetching the data currently lives here

# add the IGN tile layers
leaflet["GeoportailFrance"]["plan"] = {}
leaflet["GeoportailFrance"]["orthos"] = {}
leaflet["GeoportailFrance"]["parcels"] = {}
tilelayers_list = []
apikey_list = [
"administratif",
"agriculture",
"altimetrie",
"cartes",
"clc",
"economie",
"environnement",
"essentiels",
"lambert93",
"ocsge",
"ortho",
"orthohisto",
"satellite",
"sol",
"topographie",
"transports",
]
url_template = (
"https://wxs.ign.fr/apikey/geoportail/wmts?REQUEST=GetCapabilities&SERVICE=wmts"
)
for j in range(0, len(apikey_list)):
apikey = apikey_list[j]
url = url_template.replace("apikey", apikey)
resp = requests.get(url)
resp_dict = xmltodict.parse(resp.content)
layer_list = resp_dict["Capabilities"]["Contents"]["Layer"]
addictionnal_dict = {}
for i in range(len(layer_list)):
layer = resp_dict["Capabilities"]["Contents"]["Layer"][i]
variant = layer["ows:Identifier"]
name = ""
if "." not in variant:
name = variant.lower().capitalize()
else:
name = variant.split(".")[0].lower().capitalize()
for i in range(1, len(variant.split("."))):
name = name + "_" + (variant.split(".")[i]).lower().capitalize()
name = name.replace("-", "_")
if variant == "GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2":
name = "plan"
if variant == "ORTHOIMAGERY.ORTHOPHOTOS":
name = "orthos"
if variant == "CADASTRALPARCELS.PARCELLAIRE_EXPRESS":
name = "parcels"
if "Style" in layer:
if type(layer["Style"]) is dict:
style = layer["Style"]["ows:Identifier"]
elif type(layer["Style"]) is list:
style = layer["Style"][1]["ows:Identifier"]
else:
style = "normal"
TileMatrixSetLimits = layer["TileMatrixSetLink"]["TileMatrixSetLimits"][
"TileMatrixLimits"
]
min_zoom = int(TileMatrixSetLimits[0]["TileMatrix"])
max_zoom = int(TileMatrixSetLimits[-1]["TileMatrix"])
TileMatrixSet = layer["TileMatrixSetLink"]["TileMatrixSet"]
format = layer["Format"]
bounding_lowerleft_corner = layer["ows:WGS84BoundingBox"][
"ows:LowerCorner"
] # given with lon/lat order
bounding_upperright_corner = layer["ows:WGS84BoundingBox"][
"ows:UpperCorner"
] # given with lon/lat order
lowerleft_corner_lon, lowerleft_corner_lat = bounding_lowerleft_corner.split(
" "
)
upperright_corner_lon, upperright_corner_lat = bounding_upperright_corner.split(
" "
)
bounds = [
[float(lowerleft_corner_lat), float(lowerleft_corner_lon)],
[float(upperright_corner_lat), float(upperright_corner_lon)],
]
if format == "application/x-protobuf":
pass
elif format == "image/x-bil;bits=32":
pass
elif apikey == "lambert93":
pass
else:
tilelayers_list.append("GeoportailFrance." + name)
leaflet["GeoportailFrance"][name] = {
"url": """https://wxs.ign.fr/{apikey}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET={TileMatrixSet}&FORMAT={format}&LAYER={variant}&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}""",
"html_attribution": """<a target="_blank"href="https://www.geoportail.gouv.fr/">Geoportail France</a>""",
"attribution": "Geoportail France",
"bounds": bounds,
"min_zoom": min_zoom,
"max_zoom": max_zoom,
"apikey": apikey,
"format": format,
"style": style,
"variant": variant,
"name": "GeoportailFrance." + name,
"TileMatrixSet": TileMatrixSet,
}
possibly_broken = [
"Ocsge_Constructions_2002",
"Ocsge_Constructions_2014",
"Ocsge_Couverture_2002",
"Ocsge_Couverture_2014",
"Ocsge_Usage_2002",
"Ocsge_Usage_2014",
"Orthoimagery_Orthophotos_Coast2000",
"Pcrs_Lamb93",
"Orthoimagery_Ortho_sat_Spot_2013",
"Orthoimagery_Orthophotos_1980_1995",
]
if name in possibly_broken:
leaflet["GeoportailFrance"][name]["status"] = "broken"

@martinfleis I can have a look if needed. @salahelfarissi Don't hesitate to tell me if you need some help

@HaudinFlorence @martinfleis
My first try. Please review and let me know if I need to write proper tests.
#166

@salahelfarissi it should be covered by existing test suite