0

I need to plot an interactive map of German postal code regions. For this I would like to employ a plotly.express.choropleth map. For roughly ~7500 of the 8300 regions this works like a charm. Unfortunately, for the remaining regions plotly seems to get the orientation of the polygones wrong:

enter image description here

The problem is, that I cannot figure out, why the plot turns out wrong in plotly. I have checked the orientation of the edges (with the orientation being consistent across all 8300 regions):

enter image description here

I have verified, that the centroid of the region (as computed by geopandas) is within Germany (as opposed to the opposite side of the world): postal_codes.geometry.centroid = POINT (6.25030 51.31646).

Also, I have plotted the same region using geopandas.DataFrama.explore():

enter image description here

Any ideas anybody? :(

You can load the shape of the polygone that's depicted above as geojson:

{"type": "FeatureCollection", "features": [{"id": "4753", "type": "Feature", "properties": {"region_id": "41334"}, "geometry": {"type": "Polygon", "coordinates": [[[6.25759159551657, 51.3620329779054], [6.22674617205239, 51.365633164072], [6.22444872249862, 51.3649704207859], [6.22631894075279, 51.3605546143711], [6.1899070071525, 51.3394543938591], [6.19426159278685, 51.3348703440469], [6.1854202733527, 51.3346546140994], [6.17416028291703, 51.3330495494914], [6.16858422038773, 51.3329795447041], [6.16936418645312, 51.3293300166974], [6.16211652019435, 51.3230115842556], [6.15961744170531, 51.3196839261837], [6.15917056389077, 51.3154065142613], [6.15426110832449, 51.3109674787698], [6.15383279603901, 51.3074261455405], [6.17383815212283, 51.3003487293762], [6.17826982198397, 51.3021528124584], [6.18005353204891, 51.3017348540289], [6.18873819935719, 51.3027310873733], [6.19079932638695, 51.3002635519086], [6.19843397820144, 51.3023234558051], [6.2017794022204, 51.3010566429582], [6.21128147917417, 51.2947831765034], [6.2213423450626, 51.2857902474551], [6.22830596276523, 51.2806888925787], [6.23179209968684, 51.279350402822], [6.23433813212753, 51.2787827884543], [6.23353204822973, 51.2749297921029], [6.23268632856096, 51.2742319651821], [6.23473508555356, 51.2721932856899], [6.22866667329171, 51.2707815486825], [6.2346831900953, 51.2600263653457], [6.23616193999372, 51.2602845313032], [6.23861139422953, 51.2559467892672], [6.24142456838164, 51.2528049983325], [6.24651851073767, 51.2553575821283], [6.25009725384023, 51.2591981690455], [6.25430399589004, 51.2589534668074], [6.25686092488099, 51.2627175133241], [6.26411741903873, 51.2683911852773], [6.26592948949606, 51.272031139355], [6.26567669040929, 51.2758732586984], [6.26851021220286, 51.2796987146605], [6.27306996325305, 51.2788838585939], [6.27622052433243, 51.2843637337508], [6.30452081830391, 51.2775340580964], [6.31826906487104, 51.2772117641171], [6.32655814718194, 51.2778054169968], [6.32147959082357, 51.2862362324667], [6.3142731355951, 51.2863568477585], [6.31378780683609, 51.2933427551957], [6.3186814029799, 51.2930910489484], [6.32139755912665, 51.2976662079215], [6.31957871994332, 51.2981619580872], [6.32192835004979, 51.301587366345], [6.32066748731514, 51.3028071216022], [6.32113823139307, 51.3040496209346], [6.31834955835255, 51.3047188064445], [6.3214269180918, 51.3085053811113], [6.31890121121731, 51.312710728004], [6.31908168009925, 51.3139146874175], [6.31656345773863, 51.3148994633353], [6.31496276155627, 51.3177144912525], [6.31417286165014, 51.3179500168611], [6.31417918154349, 51.3217208514643], [6.31624500935491, 51.3222825408531], [6.31329971055241, 51.3241375954849], [6.31699157135774, 51.3254929804933], [6.3157313640447, 51.3290021495174], [6.31969641993732, 51.3293730670889], [6.31956101358546, 51.3323898521365], [6.31112299126933, 51.3334316948908], [6.31194435773355, 51.3349872817138], [6.31071378024246, 51.3363031348685], [6.3133432413171, 51.3363966743481], [6.3127517023639, 51.3395508758328], [6.3140233734103, 51.3401620005886], [6.31311844768588, 51.3457156599413], [6.3079361042153, 51.3532437135755], [6.31004589884257, 51.3543366739173], [6.30545761338133, 51.3579987281457], [6.30409165649909, 51.3579690832902], [6.30406776111985, 51.3599942227126], [6.28530005741198, 51.3595334155322], [6.28298643865312, 51.3584229525221], [6.28228594007486, 51.3582996009249], [6.28172483420492, 51.3580098360699], [6.26580932581206, 51.3549486000478], [6.26313461419479, 51.3564085456764], [6.2638262513051, 51.3566566167129], [6.25759159551657, 51.3620329779054]]]}}]}

Edit: Also tried plotting the polygon in DataStudio - same result as with plotly. The geometry was created using BigQuery ST_GEOGFROMTEXT, which per default will create the polygon of the smallest shape. So no idea, why plotly and DataStudio still get it wrong.

Edit: You can reproduce the issue using the following code. Suppose the geojson string is saved in 'sample.geojson', then run:

import geopandas as gpd
import plotly.express as px

# read data
postal_codes = gpd.read_file("sample.geojson")

# plotly get polygon orientation wrong
fig = px.choropleth(
    postal_codes,
    geojson=json.loads(postal_codes.to_json()),
    locations="region_id",
    featureidkey="properties.region_id",
)
fig.update_geos(fitbounds="locations", visible=False)
fig.show()

# geopandas gets it right
postal_codes_sample.explore()

# polygon orientation is correct
geojson = postal_codes.to_json()
polygon = json.loads(geojson)['features'][0]['geometry']['coordinates']
points = []
z = 0
for i in polygon[0]:
    if z % 2 == 1:
        points.append(i)
    z += 1
df = pd.DataFrame(points, columns=["lon", "lat"])
ax = df.plot(x="lon", y="lat", kind="scatter")
for i, point in enumerate(points):
    ax.text(point[0], point[1]+0.005, str(i))
2
  • 1
    You're more likely to get an answer if you include a code example reproducing the issue.
    – user2856
    Mar 28 at 21:18
  • Hi @user2856, you are right. I have added a short code sample that reproduces the issue.
    – J Berz
    Apr 2 at 9:26

1 Answer 1

0

This is a case of polygon bleed. You may have a geometry issue in that feature somewhere, eg a coincident point, a dangling line or self intersection. I didn't look for it, but a simple fix is:

postal_codes.geometry = postal_codes.geometry.buffer(0)

E.g.

postal_codes = gpd.read_file("sample.geojson")
postal_codes.geometry = postal_codes.geometry.buffer(0)
fig = px.choropleth(
    postal_codes,
    geojson=postal_codes,
    locations="region_id",
    featureidkey="properties.region_id",
)
fig.update_geos(fitbounds="locations", visible=False)
fig.show()

enter image description here

Note, you don't need to round-trip the gdf->json string->json object with json.loads(postal_codes.to_json()), just pass the GDF to px.choropleth.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.