Related
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:
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:
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
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()
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'])
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.
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()
When plotting with Basemap's readshapefile, if the defined map is centered anywhere else than the longitudinal center of the shapefile, only a portion of it it's plotted. Here's an example using Natural Earth's coastlines:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
shpf = './NaturalEarth/ne_50m_land/ne_50m_land'
fig, ax = plt.subplots(nrows=1, ncols=1, dpi=100)
m = Basemap(
ax = ax,
projection = 'cyl',
llcrnrlon = 0, llcrnrlat = -90,
urcrnrlon = 360, urcrnrlat = 90
)
m.readshapefile(shpf,'ne_50m_land')
m.drawmeridians(np.arange(0,360,45),labels=[True,False,False,True])
Which produces:
Is there a workaround for this with Basemap or Python? I know some people re-center the shapefile in QGIS or similar, but it seems unpractical to do so every time you create a new map, and my QGIS skills are extremely basic.
One way to do it would be to tell readshapefile not to plot the coastlines directly and then to manipulate the line segments before plotting them yourself. Here an example based on your use case:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
shpf = 'shapefiles/ne_50m_land'
fig, ax = plt.subplots(nrows=1, ncols=1, dpi=100)
m = Basemap(
ax = ax,
projection = 'cyl',
llcrnrlon = 0, llcrnrlat = -90,
urcrnrlon = 360, urcrnrlat = 90
)
m.readshapefile(shpf,'ne_50m_land', drawbounds = False)
boundary = 0.0
for info, shape in zip(m.ne_50m_land_info, m.ne_50m_land):
lons, lats = map(np.array, zip(*shape))
sep = (lons <= boundary).astype(int)
roots = np.where(sep[:-1]+sep[1:] == 1)[0]+1
lower = np.concatenate([[0],roots]).astype(int)
upper = np.concatenate([roots,[len(lons)]]).astype(int)
for low, high in zip(lower,upper):
lo_patch = lons[low:high]
la_patch = lats[low:high]
lo_patch[lo_patch<0] += 360
x,y = m(lo_patch,la_patch)
ax.plot(x,y,'k',lw=0.5)
m.drawmeridians(np.arange(0,360,45),labels=[True,False,False,True])
plt.show()
In the example above, I iterate through the line segments of the shape file the way it is explained in the Basemap documentation. First I thought it would be enough to just add 360 to each point with a longitude smaller 0, but then you would get horizontal lines whenever a coast line crosses the 0 degree line. So, instead, one has to cut the lines into smaller segments whenever such a crossing appears. This is quite easily accomplished with numpy. I then use the plot command to draw the coast lines. If you want to do something more complex have a look at the Basemap documentation.
The final result looks like this:
Hope this helps.
I have a set of satellite data file here, I created a grid for lat & lon and a 2D array for Ozone values.
I know that in order to plot the contourf of the data in a map I need the projection coordinates, but I can't get find a way around it as my grid is not square (144x24). I am covering the geographical area (0 to 360; -30 to 30) and I require square pixels.
The data is quite long to post it but this is my code so far,
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from mpl_toolkits.basemap import Basemap, cm
%matplotlib inline
path = '/home/rafaella/month_files_CSV/O3_COLUMNS_MATCHED_fv0005_200306.csv'
df = pd.read_csv(path, skiprows=1)
df = pd.read_csv(path, delim_whitespace=True)
lat = np.array(df['AVG_LAT'])
lon = np.array(df['AVG_LON'])
toc = np.array(df['TROP_COL'])
#new grid for lon[0,360] lat[-30,30]
lomin = 0
lomax = 360
lamin = -30
lamax = 30
stp = 2.5
loc_lon = np.zeros(int((lomax-lomin)/stp))
loc_lat = np.zeros(int((lamax-lamin)/stp))
for i in range(0,len(loc_lon)):
loc_lon[i] = i*stp +lomin
for j in range(0,len(loc_lat)):
loc_lat[j] = j*stp +lamin
mtoc_local = np.zeros((len(loc_lon),len(loc_lat)))
sdtoc_local = np.zeros((len(loc_lon),len(loc_lat)))
mtoc_local[:,:] = np.nan
sdtoc_local[:,:] = np.nan
for i in range (0, len(loc_lon)):
for j in range (0,len(loc_lat)):
ix = np.where((lon>=loc_lon[i])& (lat>=loc_lat[j]) & (lon<loc_lon[i]+stp) & (lat<loc_lat[j]+stp))[0]
mtoc_local[i,j]=np.nanmean(toc[ix])
sdtoc_local[i,j]=np.nanstd(toc[ix])
fig = plt.figure(figsize=(20, 5))
map = Basemap(llcrnrlon=0,llcrnrlat=-30, urcrnrlon=360.,urcrnrlat=30.,\
rsphere=(6378137.00,6356752.3142),\
resolution='l',projection='merc',\
lat_0=0,lon_0=-30.,lat_ts=30.)
map.drawcoastlines()
# draw parallels
map.drawparallels(np.arange(-30,30,10),labels=[1,1,0,1])
# draw meridians
map.drawmeridians(np.arange(-180,180,20),labels=[1,1,0,1])
map = plt.contourf(loc_lon, loc_lat , mtoc_local.T, vmin=210, vmax=350, cmap='RdPu')
plt.colorbar(orientation='horizontal', ticks=[200, 220, 240, 260, 280, 300, 320, 340] )
plt.title('Tropical TOC monthly mean 06,2009')
plt.show()
It plots very well the map OR the data but not both. here an image of both separately
map
real data
I am very new to python, I started a month ago, so it is still not familiar to me all the functions and libraries.
Your code has two problems. First you have to apply the projection on your coordinates which is done using x,y = map(lon, lat). However, this will raise an error in your case since the dimensions of loc_lon and loc_lat are different. Instead of passing x and y vectors to the contourf function you can pass arrays with the same shape as z (mtoc_local.T). You can use np.meshgrid to create those. Long story short, replace the line with the contourf command with the following three lines
X, Y = np.meshgrid(loc_lon, loc_lat)
x,y = map(X,Y)
map = plt.contourf(x, y , mtoc_local.T, vmin=210, vmax=350, cmap='RdPu')
and the result looks like this
I'm plotting wind speed as a contour plot, with latitude as the x co-ordinate and pressure as the y axis.
I want the y axis as a log scale, and this works as shown below
http://i.stack.imgur.com/aoSGN.png
How do I specify where the y ticks are, i.e at the moment it plots a tick at 1000mb, 100mb etc. How would I specify it so it plots ticks at say 1000,900,700,500,200mb etc.
I tried making an array of numbers and putting this in the ytick code but it didn't work!
Many thanks in advance.
PS Ideally it'd have a similar log tick scale as this here;
http://i.stack.imgur.com/dJCxB.jpg
Here is my code;
from netCDF4 import Dataset
import numpy as np
from matplotlib import pyplot as plt
myfile = '/home/ubuntu/Python-Postburn_Tools/out.nc'
Import = Dataset(myfile, mode='r')
print Import.variables #Display contents of NC File (i.e list variables)
print Import.variables['ua'] #Lists shape of variable e.g(time, lev, lat, lon)
lons = Import.variables['lon'][:] #Longitude
lats = Import.variables['lat'][:] #Latitude
time = Import.variables['time'][:] #Time
height = Import.variables['lev'][:] #Levels
wind = Import.variables['ua'][:]
#relax = Import.variables['var154'][:]
temp = Import.variables['ta'][:]
display = np.average(wind[12:131,:,:,0],axis=0) #Use this to plot a variable
#Visual changes
uscale = [-40,-35,-30,-25,-20,-15,-10,-5,0,5,10,15,20,25,30,35,40]
CS = plt.contourf(lats,height,display,uscale,cmap=plt.cm.jet)
cbar = plt.colorbar(CS)
cbar.ax.set_ylabel('Temperature (C)')
plt.gca().invert_yaxis()
plt.yscale('log', nonposy='clip')
plt.xticks(np.round((np.arange(-90, 91, 30)), 2))
plt.xlim(xmin=-90)
plt.xlabel('Latitude')
plt.ylabel('Pressure (hPa)')
plt.title('Year 2-11 control Temp - TRelax (C)')
plt.show()
Sorry if this question is simple I'm a newb to using Python and Basemap. Anyway I'm trying to plot the path of 20 hurricanes on a map (graph). The map itself and the legend show up perfectly but the paths of the hurricanes do not. Also I'm not getting any traceback messages but I think I have an idea of where my problem may be. Could someone please tell me where I went wrong.
Here's a sample of the csv file:
Year, Name, Type, Latitude, Longitude
1957,AUDREY,HU, 21.6, 93.3
1957,AUDREY,HU,22.0, 93.4
1957,AUDREY,HU,22.6, 93.5
1969,AUDREY,HU,28.2,99.6
1957,AUDREY,HU,26.5,93.8
1957,AUDREY,HU,27.9,93.8
1957,AUDREY,HU,29.3,95
1957,AUDREY,HU,27.9,93.8
1957,AUDREY,HU,29.3,93.8
1957,AUDREY,HU,30.7,93.5
1969,CAMILLE,HU, 21.6,99.3
1969,CAMILLE,HU,22.0,98.4
1969,CAMILLE,HU,22.6,90.5
1969,CAMILLE,HU,23.2,93.6
Here's the code I have so far:
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import csv, os, scipy
import pandas
from PIL import *
data = np.loadtxt('louisianastormb.csv',dtype=np.str,delimiter=',',skiprows=1)
'''print data'''
fig = plt.figure(figsize=(12,12))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
m.bluemarble()
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawstates(linewidth=0.5)
# Creates parallels and meridians
m.drawparallels(np.arange(10.,35.,5.),labels=[1,0,0,1])
m.drawmeridians(np.arange(-120.,-80.,5.),labels=[1,0,0,1])
m.drawmapboundary(fill_color='aqua')
color_dict = {'AUDREY': 'red', 'ETHEL': 'white', 'BETSY': 'yellow','CAMILLE': 'blue', 'CARMEN': 'green','BABE': 'purple', }
colnames = ['Year','Name','Type','Latitude','Longitude']
data = pandas.read_csv('louisianastormb.csv', names=colnames)
names = list(data.Name)
lat = list(data.Latitude)
long = list(data.Longitude)
colorName = list(data.Name)
#print lat
#print long
lat.pop(0)
long.pop(0)
colorName.pop(0)
latitude= map(float, lat)
longitude = map(float, long)
x, y = m(latitude,longitude)
#Plots points on map
for colorName in color_dict.keys():
plt.plot(x,y,linestyle ='-',label=colorName,color=color_dict[colorName], linewidth=5 )
lg = plt.legend()
lg.get_frame().set_facecolor('grey')
plt.show()
two (okay I lied, should be there) problems with your code
i, your input longitude should be negative to be within the boundary you defined for your basemap, so add this after before converting to x and y
longitude = [-i for i in longitude]
ii, your coordinate conversion line is wrong, you should swap lon and lat in the argument list
x, y = m(longitude, latitude)
instead of
x, y = m(latitude,longitude)
EDIT:
okay, the second question that OP posted in the comments, please check the complete code below and please pay attention to the changes I've made compared to yours
# Last-modified: 21 Oct 2013 05:35:16 PM
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import csv, os, scipy
import pandas
from PIL import *
data = np.loadtxt('louisianastormb.csv',dtype=np.str,delimiter=',',skiprows=1)
'''print data'''
fig = plt.figure(figsize=(12,12))
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-100.,llcrnrlat=0.,urcrnrlon=-20.,urcrnrlat=57.,
projection='lcc',lat_1=20.,lat_2=40.,lon_0=-60.,
resolution ='l',area_thresh=1000.)
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawstates(linewidth=0.5)
# m.bluemarble(ax=ax)
# Creates parallels and meridians
m.drawparallels(np.arange(10.,35.,5.),labels=[1,0,0,1])
m.drawmeridians(np.arange(-120.,-80.,5.),labels=[1,0,0,1])
m.drawmapboundary(fill_color='aqua')
color_dict = {'AUDREY': 'red', 'ETHEL': 'white', 'BETSY': 'yellow','CAMILLE': 'blue', 'CARMEN': 'green','BABE': 'purple', }
colnames = ['Year','Name','Type','Latitude','Longitude']
data = pandas.read_csv('louisianastormb.csv', names=colnames)
names = list(data.Name)
lat = list(data.Latitude)
long = list(data.Longitude)
colorNames = list(data.Name)
#print lat
#print long
lat.pop(0)
long.pop(0)
colorNames.pop(0)
latitude= map(float, lat)
longitude = map(float, long)
# added by nye17
longitude = [-i for i in longitude]
# x, y = m(latitude,longitude)
x, y = m(longitude,latitude)
# convert to numpy arrays
x = np.atleast_1d(x)
y = np.atleast_1d(y)
colorNames = np.atleast_1d(colorNames)
#Plots points on map
for colorName in color_dict.keys():
plt.plot(x[colorName == colorNames],y[colorName == colorNames],linestyle ='-',label=colorName,color=color_dict[colorName], linewidth=5 )
lg = plt.legend()
lg.get_frame().set_facecolor('grey')
plt.show()
I think your difficulty is not so much in Basemap as in the plotting. Instead of plotting the entire x/y data set you need to find the x/y points corresponding on hurricane Z. Then plot only those points in a certain color c. Then find the points corresponding to the next hurricane etc...
The below, while not using the Basemap data structure should provide a starting point for plotting subsets of points based on some selector vector.
#given a list of x,y coordinates with a label we'll plot each line individually
#first construct some points to plot
x1 = [1,1.1,1.2,1.3, 2.0,2.2,2.3, 4,3.9,3.8,3.7]
y1 = [5,5.1,5.2,5.3, 6.0,6.2,6.3, 2,2.1,2.2,2.3]
pointNames = []
#generate some labels
pointNames.extend(['a']*4)
pointNames.extend(['b']*3)
pointNames.extend(['c']*4)
#make things easy by casting to numpy arrays to allow for easier indexing
x1 = numpy.array(x1)
y1 = numpy.array(y1)
pointNames = numpy.array(pointNames)
for elem in ['a','b','c']:
selector = pointNames==elem
subsetX = x1[selector]
subsetY = y1[selector]
#now plot subsetX vs subsetY in color Z
plot(subsetX,subsetY,'*-')
show()