3

I have some coordinates from Lng and lat, that I combined to a Linestring.

The Linestring is made of 2 Points. From one point starting=origin and from one point ending = destination.

This is the code for the Linestring column

erg2['Linestring'] = erg2.apply(lambda x: LineString([(x['latitude_origin'], x['longitude_origin']), (x['latitude_destination'], x['longitude_destination'])]), axis = 1)

I am trying everything to plot the Linestring from origin to destination without any good results..\

You would help me out a lot!\

This is the dataframe erg2 with lng and lat and the combined Linestrings... How can I plot them over a map. The numbers in the columns of origin and destination are location ids of a city..

What would you as an experienced programmer plot. The two points with scatter or the combined Linestrings?? Following I am putting in my dataframe.head().

Usually the df has [19600 rows x 8 columns]

origin destination move longitude_origin latitude_origin
88 88 20 13.481016 52.457055
88 89 0 13.481016 52.457055
88 110 0 13.481016 52.457055
88 111 0 13.481016 52.457055
88 112 0 13.481016 52.457055
87 83 0 13.479667 52.479600
longitude_destination latitude_destination Linestring
13.481016 52.457055 LINESTRING (52.45705489204205 13.4810161067992...
13.504075 52.443923 LINESTRING (52.45705489204205 13.4810161067992...
13.613772 52.533194 LINESTRING (52.45705489204205 13.4810161067992...
13.586891 52.523562 LINESTRING (52.45705489204205 13.4810161067992...
13.559341 52.507418 LINESTRING (52.45705489204205 13.4810161067992...
13.481016 52.457055 LINESTRING (52.45705489204205 13.4810161067992...

I am trying out to plot the Lines with the sample code from geopandas from @RobRaymond

The result shows lines which dont make sense.. This is the output:

enter image description here

All the lines have this description in the hover all start at 87-... In my dataframe you see that we have origin 88 etc...

It is also important to plot the Lines depending on the movements. Since zero movements don't need to be plotted... I really hope that I am making the question clear. It is really getting above my head a bit.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Rebecka
  • 51
  • 1
  • 6
  • I'm looking at the added images, but with such a dense population, destinations, routes and their frequency are all lost. The steps you take will depend on your priorities. – r-beginners Sep 27 '21 at 12:10
  • I wrote another question. maybe the problem gets more clear. Thousands of thank you for your help. I am getting really frustrated. https://stackoverflow.com/questions/69396739/plotting-ways-linestrings-over-a-map-in-python – Rebecka Sep 30 '21 at 17:47

2 Answers2

1
import requests, io, json
import geopandas as gpd
import shapely.geometry
import pandas as pd
import numpy as np
import itertools
import plotly.express as px

# get some public addressess - hospitals.  data that has GPS lat / lon
dfhos = pd.read_csv(io.StringIO(requests.get("http://media.nhschoices.nhs.uk/data/foi/Hospital.csv").text),
    sep="¬",engine="python",).loc[:, ["OrganisationName", "Latitude", "Longitude"]]

a = np.arange(len(dfhos))
np.random.shuffle(a)
# establish N links between hospitals
N = 10
df = (
    pd.DataFrame({0:a[0:N], 1:a[25:25+N]}).merge(dfhos,left_on=0,right_index=True)
    .merge(dfhos,left_on=1, right_index=True, suffixes=("_origin", "_destination"))
)

# build a geopandas data frame that has LineString between two hospitals
gdf = gpd.GeoDataFrame(
    data=df,
    geometry=df.apply(
        lambda r: shapely.geometry.LineString(
            [(r["Longitude_origin"], r["Latitude_origin"]),
             (r["Longitude_destination"], r["Latitude_destination"]) ]), axis=1)
)

# sample code https://plotly.com/python/lines-on-mapbox/#lines-on-mapbox-maps-from-geopandas
lats = []
lons = []
names = []

for feature, name in zip(gdf.geometry, gdf["OrganisationName_origin"] + " - " + gdf["OrganisationName_destination"]):
    if isinstance(feature, shapely.geometry.linestring.LineString):
        linestrings = [feature]
    elif isinstance(feature, shapely.geometry.multilinestring.MultiLineString):
        linestrings = feature.geoms
    else:
        continue
    for linestring in linestrings:
        x, y = linestring.xy
        lats = np.append(lats, y)
        lons = np.append(lons, x)
        names = np.append(names, [name]*len(y))
        lats = np.append(lats, None)
        lons = np.append(lons, None)
        names = np.append(names, None)

fig = px.line_mapbox(lat=lats, lon=lons, hover_name=names)

fig.update_layout(mapbox_style="stamen-terrain",
                  mapbox_zoom=4,
                  mapbox_center_lon=gdf.total_bounds[[0,2]].mean(),
                  mapbox_center_lat=gdf.total_bounds[[1,3]].mean(),
                  margin={"r":0,"t":0,"l":0,"b":0}
                 )

simplified version

  • using dataframe of links between hospitals as sample data
  • plotly documentation and labels clearly note need to delimit lines with None in arrays passed to px.line_mapbox(). construct these in a far more direct way using numpy there is no need to construct LineString, use geopnadas or shapely
# plotly takes array delimited with None between lines. Use numpy padding and shaping to generate this array
# from pair of features
def line_array(df, cols):
    return np.pad(df.loc[:,cols].values, [(0, 0), (0, 1)], constant_values=None).reshape(1,(len(df)*3))[0]
    

fig = px.line_mapbox(lat=line_array(df, ["Latitude_origin", "Latitude_destination"]), 
                     lon=line_array(df, ["Longitude_origin", "Longitude_destination"]),
                     hover_name=line_array(df, ["OrganisationName_origin", "OrganisationName_destination"]),
)

fig.update_layout(mapbox_style="stamen-terrain",
                  mapbox_zoom=4,
                  mapbox_center_lon=df.loc[:,["Longitude_origin","Longitude_destination"]].mean().mean(),
                  mapbox_center_lat=df.loc[:,["Latitude_origin","Latitude_destination"]].mean().mean(),
                  margin={"r":0,"t":0,"l":0,"b":0}
                 )


enter image description here

Rob Raymond
  • 29,118
  • 3
  • 14
  • 30
  • Hello @Rob Raymond, first of all thanks a lot for helping me out. I just found a moment to work on my code again. Despite your help I still need to understand some points... What I really like about the longer version is that the hover name gives information about the start and ending point. This is one information I really want to keep in my work. Altought your example works perfectly it doesnt work for me at all... – Rebecka Sep 24 '21 at 19:22
  • the name is always same origin but different destination – Rebecka Sep 24 '21 at 19:30
  • update your question with your sample data as text and I can demonstrate how it works. the shorter version can easily be adapted to have start and end location in `hover_name`. I can also adapt the sample data I'm generating to be same starting point and multiple different ending points. there is also another candidate solution, which is plot the lines as a geojson layer in the layout – Rob Raymond Sep 24 '21 at 21:26
0

Using the examples and data from the official plotly reference, I have replaced your questions and identified the issues that prevent line segments from being drawn. You need to specify a column of text that indicates the group to be set in color. So set the color to the column that is the group key in your data.

import plotly.express as px
import pandas as pd

df_airports = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')
df_airports.head()

df_flight_paths = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv')
df_flight_paths.sort_values('cnt', ascending=False, inplace=True)

df_flight_paths = df_flight_paths.query("airport2 in ['DFW','LAX']")

df_flight_paths.head()
start_lat   start_lon   end_lat     end_lon     airline     airport1    airport2    cnt
33  33.942536   -118.408074     32.895951   -97.0372    AA  LAX     DFW     914
50  29.533694   -98.469778  32.895951   -97.0372    AA  SAT     DFW     826
44  41.979595   -87.904464  32.895951   -97.0372    AA  ORD     DFW     825
20  30.194533   -97.669872  32.895951   -97.0372    AA  AUS     DFW     820
19  33.640444   -84.426944  32.895951   -97.0372    AA  ATL     DFW     580

fig = px.line_mapbox(df_flight_paths,
                     lat="start_lat", 
                     lon="start_lon", 
                     color="airport2",
                     hover_name="airport1",
                     #animation_frame="time",
                     center =dict(lon=-97.0,lat=38.0),
                     zoom=3, height=600
                    )

fig.update_layout(mapbox_style="stamen-terrain",
                  mapbox_zoom=4,
                  mapbox_center_lon=-97.0,
                  mapbox_center_lat=38.0,
                  margin={"r":0,"t":0,"l":0,"b":0}
                 )

fig.show()

enter image description here

r-beginners
  • 31,170
  • 3
  • 14
  • 32
  • `fig.data` It is useful to keep this in mind as you can use it to check the data structure being rendered. If my answer is helpful to you, please click the check mark to accept the answer. – r-beginners Sep 23 '21 at 03:43