Search code examples
pythonpandasshapefilechoropleth

Plotly Choropleth Map not displaying GeoJSON Data Correctly - Possible Geometry or Mapping Issue


I'm quite new to choropleth_mapbox() and I don't know where the problem is in my script. I would like to display the average spot market energy prices in individual bidding zones. Ultimately, I would like to have a field with buttons next to such a map, thanks to which I can select the appropriate year, month or day, and the map will change its colors and display data when I hover the mouse over it. Something similar can be found e.g. here: https://www.energy-charts.info/charts/price_average_map/chart.htm?l=en&c=EU. To display such data, you first need .shp or .geojson files, which I downloaded from https://github.com/EnergieID/entsoe-py/tree/master/entsoe/geo/geojson. I imported them all into a GIS program (QGIS) and combined them into one file with an attribute table. Bidding zones coupled in GIS program

My workaround and steps taken

However, I was having trouble displaying this data, so I decided that as a test, I would make a very simple way of displaying this data with randomly generated numbers. However, it turned out that everything was not displayed correctly and some random shapes were generated on the map, not reflecting the bidding zones.

steps taken

  1. Loaded the shapefile using GeoPandas.
  2. Ensured the 'zoneName' column is present.
  3. Reprojected the GeoDataFrame to EPSG:4326.
  4. Cleaned the GeoDataFrame to keep only necessary columns ('zoneName' and 'geometry').
  5. Converted the GeoDataFrame to GeoJSON.
  6. Used Plotly's px.choropleth_mapbox to create the map.

Python code:

import pandas as pd
from pathlib import Path
import json
import geopandas as gpd
import plotly.express as px
import numpy as np

#Path to file
curr_dir = Path.cwd()
project_root = Path(curr_dir.parent)
shapefile_file = project_root / 'Dane' / 'BIDDING ZONES2.shp'

#Loading shapefile
bidding_zones= gpd.read_file(shapefile_file)

# generation of random numbers from 0 to 100 in the same amount as number of bidding zones
num_zones = len(bidding_zones_test)
random_numbers = np.random.rand(num_zones) * 100

#Creation of DataFrame, which would be necessary in choropleth_mapbox()
#Mapping of zoneName (key) of each bidding zone with random values to display later on
df = pd.DataFrame({
    'zoneName': bidding_zones_test['zoneName'],
    'random_value': random_numbers

# Convert GeoDataFrame to GeoJSON
countries = json.loads(bidding_zones.to_json())
})

# Create an interactive map
fig = px.choropleth_mapbox(df,
    geojson=countries,
    locations='zoneName',
    featureidkey="properties.zoneName",
    color='random_value',
    color_continuous_scale="Viridis",
    range_color=(0, 12),
    mapbox_style="carto-positron",
    zoom=0.5,
    center={"lat": 52.0, "lon": 19.0},  # Center on Poland
    opacity=0.2
)

fig.update_layout(
    title_text='Test Display of Shapefile',
    title_x=0.5
)

fig.show()

Outcome

I was looking for a solution in other threads, e.g.: Plotly Choropleth_mapbox plots with Dash interactivity , but i did not understand this fully cause this .shp file was not generated like mine. Between the shp file and the data in the df data frame there is a key in the form 'zoneName', but it did not map properly, look down here of the output of fig.show() Problem choropleth_mapbox()

Geojson structure

TOP of Geojson

{
    "type": "FeatureCollection",
    "features": [
        {
            "id": "0",
            "type": "Feature",
            "properties": {
                "zoneName": "AT",
                "layer": "AT",
                "path": "C:/Users/User/Downloads/AT.geojson"
            },
            "geometry": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [
                            348557.28869994543,
                            83876.34420647845
                        ],
                        [
                            349103.56787464337,
                            78661.40107473545
                        ],

Solution

  • Yes !. Thank you @Bera for leading me on the right track. You was right!. Problem was in my generated QGIS file and projection of those countries bidding zones. I made couple steps back and load all geojson area files once again to QGIS and then enter image description here

    After that i tried 'Merge vector layer' option inside QGIS and select proper projection which was EPSG 4326. Then load the file to my python script and voila! That is the effect :)

    Efekt

    I also changed couple things in my python code. I decided to load directly from geojson, not from shapefile, but it believe in the end it was no matter at all. Now i will move to another steps - try to load my average prices for each bidding country :)