4

I am trying to plot some latitude and longitudes on the map of delhi which I am able to do by using a shape file in python3.8 using geopandas Here is the link for the shape file:

https://drive.google.com/file/d/1CEScjlcsKFCgdlME21buexHxjCbkb3WE/view?usp=sharing

Following is my code to plot points on the map:

lo=[list of longitudes]
la=[list of latitudes]

delhi_map = gpd.read_file(r'C:\Users\Desktop\Delhi_Wards.shp')
fig,ax = plt.subplots(figsize = (15,15))
delhi_map.plot(ax = ax)
geometry = [Point(xy) for xy in zip(lo,la)]
geo_df = gpd.GeoDataFrame(geometry = geometry)
print(geo_df)
g = geo_df.plot(ax = ax, markersize = 20, color = 'red',marker = '*',label = 'Delhi')
plt.show()

Following is the result:

enter image description here

Now this map is not very clear and anyone will not be able to recognise the places marked so i tried to use basemap for a more detailed map through the following code:

df = gpd.read_file(r'C:\Users\Jojo\Desktop\Delhi_Wards.shp')
new_df = df.to_crs(epsg=3857)
print(df.crs)
print(new_df.crs)
ax = new_df.plot()
ctx.add_basemap(ax)
plt.show()

And following is the result:

enter image description here

I am getting the basemap but my shapefile is overlapping it. Can i get a map to plot my latitudes and longitudes where the map is much more detailed with names of places or roads or anything similar to it like in google maps or even something like the map which is being overlapped by the blue shapefile map?

Is it possible to plot on a map like this??

https://www.researchgate.net/profile/P_Jops/publication/324715366/figure/fig3/AS:618748771835906@1524532611545/Map-of-Delhi-reproduced-from-Google-Maps-12.png

gm1312
  • 99
  • 1
  • 1
  • 11
  • It is. 1)You load the image as a background in matplotlib. 2) Starting from the map, you compute the coordinates (latitude and longitude) that comes with it as a pyhton function. Yhe function establishs the relation between the coordinates and the dimensions of your axes in the matplotlib figure. 3) You plot your points (as matplotlib points) using the coordinates given by the python function. – Daniel N Aug 29 '20 at 07:08
  • could you please elaborate as I am fairly new to matplotlib and geopandas so having difficulties in understanding? @DanielN – gm1312 Aug 29 '20 at 08:52

2 Answers2

4

use zorder parameter to adjust the layers' orders (lower zorder means lower layer), and alpha to the polygon. anyway, I guess, you're plotting df twice, that's why it's overlapping.

here's my script and the result

import geopandas as gpd
import matplotlib.pyplot as plt
import contextily as ctx
from shapely.geometry import Point

long =[77.2885437011719, 77.231931, 77.198767, 77.2750396728516]
lat = [28.6877899169922, 28.663863, 28.648287, 28.5429172515869]
geometry = [Point(xy) for xy in zip(long,lat)]


wardlink = "New Folder/wards delimited.shp"

ward = gpd.read_file(wardlink, bbox=None, mask=None, rows=None)
geo_df = gpd.GeoDataFrame(geometry = geometry)

ward.crs = {'init':"epsg:4326"}
geo_df.crs = {'init':"epsg:4326"}

# plot the polygon
ax = ward.plot(alpha=0.35, color='#d66058', zorder=1)
# plot the boundary only (without fill), just uncomment
#ax = gpd.GeoSeries(ward.to_crs(epsg=3857)['geometry'].unary_union).boundary.plot(ax=ax, alpha=0.5, color="#ed2518",zorder=2)
ax = gpd.GeoSeries(ward['geometry'].unary_union).boundary.plot(ax=ax, alpha=0.5, color="#ed2518",zorder=2)

# plot the marker
ax = geo_df.plot(ax = ax, markersize = 20, color = 'red',marker = '*',label = 'Delhi', zorder=3)

ctx.add_basemap(ax, crs=geo_df.crs.to_string(), source=ctx.providers.OpenStreetMap.Mapnik)
plt.show()

the result


I don't know about google maps being in the contextily, I don't think it's available. alternatively, you can use OpenStreetMap base map which shows quite the same toponym, or any other basemap you can explore. use `source` keyword in the argument, for example, `ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik)` . here's how to check the available providers and the map each providers provides:
>>> ctx.providers.keys()
dict_keys(['OpenStreetMap', 'OpenSeaMap', 'OpenPtMap', 'OpenTopoMap', 'OpenRailwayMap', 'OpenFireMap', 'SafeCast', 'Thunderforest', 'OpenMapSurfer', 'Hydda', 'MapBox', 'Stamen', 'Esri', 'OpenWeatherMap', 'HERE', 'FreeMapSK', 'MtbMap', 'CartoDB', 'HikeBike', 'BasemapAT', 'nlmaps', 'NASAGIBS', 'NLS', 'JusticeMap', 'Wikimedia', 'GeoportailFrance', 'OneMapSG'])
>>> ctx.providers.OpenStreetMap.keys()
dict_keys(['Mapnik', 'DE', 'CH', 'France', 'HOT', 'BZH'])
sutan
  • 404
  • 3
  • 14
  • It gives the following error : `Traceback (most recent call last): ax = geo_df.to_crs(epsg=3857).plot(ax = ax, markersize = 20, color = 'red',marker = '*',label = 'Delhi', zorder=2) File "C:\Users\Jojo\AppData\Local\Programs\Python\Python38\lib\site-packages\geopandas\geodataframe.py", line 816, in to_crs geom = df.geometry.to_crs(crs=crs, epsg=epsg) File "Python38\lib\site-packages\geopandas\geoseries.py", line 526, in to_crs raise ValueError( ValueError: Cannot transform naive geometries. Please set a crs on the object first` @sutan – gm1312 Aug 29 '20 at 17:04
  • Also if I remove the `.to_crs(epsg=3857)` from the ax variable line then I get the world map instead. Here is the map I'm getting after removing the part I mentioned above: [link](https://drive.google.com/file/d/1C2Jc8cCW-YJHduTiWJQuaRP4mIBoiVh6/view?usp=sharing) – gm1312 Aug 29 '20 at 17:09
  • set the `crs` first before `.to_crs(epsg=3857)`; like this, `gdf.crs = {"init":"epsg:4326"}`, then `newgdf = gdf.to_crs(epsg=3857)`, then you can plot the `newgdf.plot(alpha=0.4, zorder=1, ax=ax)`. About the world map, i think the problem is the extent? you can set the `ax.set_xlim` and `ax.set_ylim` with the bound of your ward `gdf`. Remember to convert the bound for the limit from epsg=4326 to 3857! – sutan Aug 30 '20 at 04:00
  • I apologize but I am still really confused as I am very new to geopandas as well as matplotlib. Following is my code: `df = gpd.read_file(r'C:\Users\Jojo\Desktop\Pyton_Folder\Map_LatLong\delhi\Delhi_Wards.shp') new_df = df.to_crs(epsg=3857) print(df.crs) print(new_df.crs) ax = new_df.plot(zorder=1, alpha=0.4) ctx.add_basemap(ax) plt.show()` This is the file I used: [link](https://drive.google.com/file/d/1Mkz_PQUDJ7I_TTfQ6cSG766dI7pYQE6r/view?usp=sharing) – gm1312 Aug 30 '20 at 15:06
  • This is the final output I got on which I want to plot my latitude and longitude: [link](https://drive.google.com/file/d/1chBF8Pu9r-GLeWgtJ9TaSWkb75WuKqfF/view) These are the list of lat ,long I want to plot on this final output: 'long =[77.2885437011719, 77.231931, 77.198767, 77.2750396728516,]' 'lat = [28.6877899169922, 28.663863, 28.648287, 28.5429172515869,]' @sutan – gm1312 Aug 31 '20 at 09:17
  • @gm1312 I don't think google maps provide the tile service freely, alternatively OpenStreetMap does. you should add `source` argument in the contextily's `add_basemap` function. I've edited my answer with the long and lat you provided and how to vary the basemap. – sutan Sep 01 '20 at 04:55
  • Thanks for all your efforts. This has really helped me. I just had one last question,If you see the first plot in which I had plotted the points,the x-y axis showed the lat long values accurately but using basemaps has changed the values on both axes and now if I hover over the points I do not get the lat long position . Is there any solution for it as well?? @sutan – gm1312 Sep 01 '20 at 08:40
  • that is because contextily base map is based on epsg 3857 which is not lat long. the matplotlib figure translates the lat-long into epsg 3857 projection. To plot with epsg 4326 (lat long), then you should remove all `to_crs(epsg=3857)`, and add argument `crs` in the `add_basemap` like this `ctx.add_basemap(ax, crs=geo_df.crs.to_string())`. I've edited my answer, notice there is no `to_crs(epsg=3857)` method. please read [contextily's guide](https://contextily.readthedocs.io/en/latest/warping_guide.html) for the basemap – sutan Sep 01 '20 at 12:18
-2

I don't know geopandas. The idea I'm suggesting uses only basic python and matplotlib. I hope you can adapt it to your needs.

The background is the following map. I figured out the GPS coordinates of its corners using google-maps.

enter image description here

The code follows the three points of my remark. Note that the use of imread and imshow reverses the y coordinate. This is why the function coordinatesOnFigur looks non-symmetrical in x and y.

Running the code yields the map with a red bullet near Montijo (there is a small test at the end).

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import patches
from matplotlib.widgets import Button


NE = (-8.9551, 38.8799)
SE = (-8.9551, 38.6149)
SW = (-9.4068, 38.6149)
NW = (-9.4068, 38.8799)

fig = plt.figure(figsize=(8, 6))
axes = fig.add_subplot(1,1,1, aspect='equal')
img_array = plt.imread("lisbon_2.jpg")
axes.imshow(img_array)

xmax = axes.get_xlim()[1]
ymin = axes.get_ylim()[0]  # the y coordinates are reversed, ymax=0

# print(axes.get_xlim(), xmax)
# print(axes.get_ylim(), ymin)


def coordinatesOnFigure(long, lat, SW=SW, NE=NE, xmax=xmax, ymin=ymin):
    px = xmax/(NE[0]-SW[0])
    qx = -SW[0]*xmax/(NE[0]-SW[0])
    py = -ymin/(NE[1]-SW[1])
    qy = NE[1]*ymin/(NE[1]-SW[1])
    return px*long + qx, py*lat + qy


# plotting a red bullet that corresponds to a GPS location on the map
x, y = coordinatesOnFigure(-9, 38.7)
print("test: on -9, 38.7 we get", x, y)
axes.scatter(x, y, s=40, c='red', alpha=0.9)

plt.show()
Daniel N
  • 127
  • 3