Obtaining coordinates in projected map using Cartopy - python-3.x

I'm trying to obtain the coordinates of the features of a map using Cartopy but I would like to obtain the map projected coordinates instead of the data from the original projection.
For instance:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection=ccrs.epsg(3857))
fig.add_axes(ax)
ax.coastlines()
ax.set_global()
lines = ax.plot((0, 360), (-85.06, 85.06), transform=ccrs.PlateCarree())
fig.show()
The previous code shows a map with two lines using the map projection but lines (a list with matplotlib.lines.Line2D instances) is just only one object with the coordinates in the original projection of the data (lines[0].get_data() ---> (array([ 0, 360]), array([-85.06, 85.06]))).
On an interactive plot, a Qt5 backend obtained after fig.show(), I can see coordinates in EPSG:3857 and in PlateCarree when the cursor is over the map so I wonder if there is an easy way to get lines in EPSG:3857 coordinates.
EDIT: The example above is quite simplified. I've tried to do it simple for better understanding but maybe is better to show the real problem.
I have a grid of data with longitudes in the range [0, 360]. I can modify the arrays in order to have inputs in the range [-180, 180] and I'm using Cartopy/Matplotlib to plot contours. From the contours I'm obtaining a matplotlib.contour.QuadContourSet with several matplotlib.collections.LineCollection. From each matplotlib.collections.LineCollection I can obtain the matplotlib.path.Paths and I would like to have the coordinates of each Path in EPSG:3857 instead of in the original PlateCarree so I can use cartopy.mpl.patch.path_to_geos to convert each Path to a shapely geometry object in the EPSG:3857 projection without having to extract vertices from each Path, convert them from PlateCarree to EPSG:3857 and then create a new Path with the converted coordinates to use cartopy.mpl.patch.path_to_geos to obtain geometries in the crs I need.

The question asks for a coordinate transformation using Cartopy's feature, and maybe something else.
Here I provide the code that performs coordinate transformation and computation check.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np
# Test data in geographic lon, lat (degrees)
lons = np.array((0, 360.01)) # any number of longitude
lats = np.array((-85.06, 85.06)) # .. longitude
# define all CRS
crs_longlat = ccrs.PlateCarree()
crs_3857 = ccrs.epsg(3857)
# Transformation function
def coordXform(orig_crs, target_crs, x, y):
"""
Converts array of (y,x) from orig_crs -> target_crs
y, x: numpy array of float values
orig_crs: source CRS
target_crs: target CRS
"""
# original code is one-liner
# it leaves an open axes that need to plt.close() later
# return plt.axes( projection = target_crs ).projection.transform_points( orig_crs, x, y )
# new improved code follows
xys = plt.axes( projection = target_crs ).projection.transform_points( orig_crs, x, y )
# print(plt.gca()) # current axes: GeoAxes: _EPSGProjection(3857)
plt.close() # Kill GeoAxes
# print(plt.gca()) # AxesSubplot (new current axes)
return xys
# Transform geographic (lon-lat) to (x, y) of epsg(3857)
xys = coordXform(crs_longlat, crs_3857, lons, lats)
for ea in xys:
print("(x, y) meters: " + str(ea[0]) + ', ' + str(ea[1]))
#Output(1)
#(x, y) meters: 0.0, -20006332.4374
#(x, y) meters: 1113.19490794, 20006332.4374
# Computation check
# Transform (x, y) of epsg(3857) to geographic (lon-lat), degrees
xs = xys[:,0] # all x's
ys = xys[:,1] # all y's
lls = coordXform(crs_3857, crs_longlat, xs, ys)
for ea in lls:
print("(lon, lat) degrees: " + str(ea[0]) + ', ' + str(ea[1]))
#Output(2)
#(lon, lat) degrees: 0.0, -85.06
#(lon, lat) degrees: 0.01, 85.06
# plt.close() # no need now
Edit 2
According to the constructive comments, the transformation function above can be written as follows:
def coordXform(orig_crs, target_crs, x, y):
return target_crs.transform_points( orig_crs, x, y )

Related

Datashader integration for polygons in plotly mapbox

I'm using plotly's Scattermapbox to overlay a map with a shaded image of polygons created by datashader's shade function (based on https://plotly.com/python/datashader/), but the projections do not seem to align, see picture below. Any suggestions how I can overcome this problem using plotly's Scattermapbox and datashader?
Reproducible example:
import geopandas as gpd
import plotly.graph_objects as go
import spatialpandas as spd
import datashader as ds
from colorcet import fire
import datashader.transfer_functions as tf
# load data
world = gpd.read_file(
gpd.datasets.get_path('naturalearth_lowres')
)
# world = world.to_crs(epsg=3857)
# create spatialpandas DataFrame
df_world = spd.GeoDataFrame(world)
# create datashader canvas and aggregate
cvs = ds.Canvas(plot_width=1000, plot_height=1000)
agg = cvs.polygons(df_world, geometry='geometry', agg=ds.mean('pop_est'))
# create shaded image
tf.shade(agg, cmap=fire)
shaded image
# create shaded image and convert to Python image
img = tf.shade(agg, cmap=fire)[::-1].to_pil()
coords_lat, coords_lon = agg.coords["y"].values, agg.coords["x"].values
# Corners of the image, which need to be passed to mapbox
coordinates = [
[coords_lon[0], coords_lat[0]],
[coords_lon[-1], coords_lat[0]],
[coords_lon[-1], coords_lat[-1]],
[coords_lon[0], coords_lat[-1]],
]
fig = go.Figure(go.Scattermapbox())
fig.update_layout(
mapbox_style="open-street-map",
mapbox_layers=[
{
"sourcetype": "image",
"source": img,
"coordinates": coordinates,
}
]
)
fig.show()
overlayed map
I read that Scattermapbox only supports Mercator projection which I found confusing as the examples in plotly's documentation seem to be in long/lat format, but I tried converting the coordinates of the GeoDataFrame to epsg 3857, see
# world = world.to_crs(epsg=3857)
The results is that the shaded image becomes invisible. Any help would be highly appreciated.
Have you tried with epsg:4326? In my case, I use this one and the geometries are placed correctly.
On the other hand, with geopandas to convert the geometry column of the dataframe you have to use the parameter "inplace=True".
We have discovered the solution to this issue: Below is each step / function code and description:
Imports for reference :
import datashader as ds
import datashader.transfer_functions as tf
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import rasterio
import shapely.geometry
import xarray as xr
_helper_add_pseudomercator_optimized : Creates array from the meshgrid with the proper mercator coordinates from the original raster with epsg:4326.
def _helper_add_pseudomercator_optimized(raster):
"""Adds mercator coordinates epsg:3857 from a raster with epsg:4326.
Originally defined as `add_psuedomercator_adam_manuel_optimized`
Args:
raster: xr.DataArray: `xr.DataArray` to transform coordinates
Returns:
`xr.DataArray` with coordinates (x, y) transformed from epsg:4326 to epsg:3857
"""
# Transformer that converts coordinates from epsg 4326 to 3857
gcs_to_3857 = Transformer.from_crs(4326, 3857, always_xy=True)
x_vals = list(raster.x.values.squeeze()) # x values from the raster dimension x
y_vals = list(raster.y.values.squeeze()) # x values from the raster dimension x
# Allows transformation of non-square coordinates
y_dummy_vals = [raster.y.values[0] for v in raster.x.values] # dummy values
x_dummy_vals = [raster.x.values[0] for v in raster.y.values] # dummy values
x, _ = gcs_to_3857.transform(x_vals, y_dummy_vals) # Obtain x output here only
_, y = gcs_to_3857.transform(x_dummy_vals, y_vals) # Obtain y output here only\
# Create meshgrid with the x and y mercator converted coordinates
lon, lat = np.meshgrid(x, y)
# Add meshgrid to raster -> raster now has mercator coordinates for every point
raster["x_mercator"] = xr.DataArray(lon, dims=("y", "x"))
raster["y_mercator"] = xr.DataArray(lat, dims=("y", "x"))
return raster
def _helper_affine_transform(raster):
"""Create new affine from a raster. Used to get new affine from the transformed affine.
Args:
raster: xr.DataArray: `xr.DataArray` to get the original affine and then transform
Returns:
New affine transform for a coarsened array
"""
res = (raster.x[-1].values - raster.x[0].values) / raster.x.shape[0]
scale = Affine.scale(res, -res)
transform = (
Affine.translation(raster.x[0].values - res / 2, raster.y[0].values - res / 2)
* scale
)
return transform
def _helper_to_datashader_quadmesh(raster, y="lat", x="lon"):
"""Create lower level quadmesh with data based on flood raster. Map Flooding
to lower level map.
Args:
raster: xr.DataArray: `xr.DataArray` raster of flooded regions
Returns:
`datashader.Canvas` based on quadmesh from original flood raster
"""
cvs = ds.Canvas(plot_height=5000, plot_width=5000)
z = xr.DataArray(
raster.values.squeeze(),
dims=["y", "x"],
coords={
"Qy": (["y", "x"], raster[y].values),
"Qx": (["y", "x"], raster[x].values),
},
name="z",
)
return cvs.quadmesh(z, x="Qx", y="Qy")
def _helper_img_coordinates(raster):
"""Get coordinates of the corners of the baseline raster.
Args:
raster: xr.DataArray: `xr.DataArray` to get corner coordinates from
Returns:
coordinates of where to plot the flooded raster on the map
"""
coords_lat, coords_lon = (raster.y.values, raster.x.values)
if len(coords_lat.shape) > 1:
coords_lat = coords_lat[:, 0]
coords_lon = coords_lon[0, :]
coordinates = [
[coords_lon[0], coords_lat[0]],
[coords_lon[-1], coords_lat[0]],
[coords_lon[-1], coords_lat[-1]],
[coords_lon[0], coords_lat[-1]],
]
return coordinates
All operations together for the below sequence :
# Add mercator coordinates to the raster
raster = _helper_add_pseudomercator_optimized(raster)
# Create quadmesh from the burned raster
agg_mesh = _helper_to_datashader_quadmesh(raster, x="x_mercator", y="y_mercator")
# Don't plot values where the flooding is zero
agg_mesh = agg_mesh.where(agg_mesh < 0)
# Convert to datashader shade
im = tf.shade(agg_mesh, Theme.color_scale)
# Convert to image
img = im.to_pil()
# Get coordinates to plot raster on map
coordinates = _helper_img_coordinates(baseline_raster)
Then this image produced by datashader can be added to a plotly plot using the plotly objects layer, and providing this layer to the figure
layer = go.layout.mapbox.Layer(
below="water",
coordinates=coordinates,
sourcetype="image",
source=img,
)

How to plot computed values?

How to plot force lines one by one? Now, the slice plots an equal part of all force lines.
The desired result is to plot the shortest lines completely and do not draw the big force lines.
The code is:
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import ode as ode
# Class for defining charge's position and magnitude
class charge:
def __init__(self, q, pos):
self.q=q
self.pos=pos
def E_point_charge(q, a, x, y):
return q*(x-a[0])/((x-a[0])**2+(y-a[1])**2)**(1.5), \
q*(y-a[1])/((x-a[0])**2+(y-a[1])**2)**(1.5)
def E_total(x, y, charges):
Ex, Ey=0, 0
for C in charges:
E=E_point_charge(C.q, C.pos, x, y)
Ex=Ex+E[0]
Ey=Ey+E[1]
return [ Ex, Ey ]
def E_dir(t, y, charges):
Ex, Ey=E_total(y[0], y[1], charges)
n=np.sqrt(Ex**2+Ey*Ey)
return [Ex/n, Ey/n]
# Defining charges
charges=[ charge(-2, [-0.1, 0]), charge(2, [0.1, 0])]
# Calculation of field lines
x0, x1=-3, 3
y0, y1=-3, 3
R=0.01
# Loop over all charges
xs,ys = [],[]
for C in charges:
# plot field lines starting in current charge
dt=0.8*R
if C.q<0:
dt=-dt
# loop over field lines starting in different directions around current charge
for alpha in np.linspace(0, 2*np.pi*15/16, 16):
r=ode(E_dir)
r.set_integrator('vode')
r.set_f_params(charges)
x=[ C.pos[0] + np.cos(alpha)*R ]
y=[ C.pos[1] + np.sin(alpha)*R ]
r.set_initial_value([x[0], y[0]], 0)
while r.successful():
r.integrate(r.t+dt)
x.append(r.y[0])
y.append(r.y[1])
hit_charge=False
# check if field line left drwaing area or ends in some charge
for C2 in charges:
if np.sqrt((r.y[0]-C2.pos[0])**2+(r.y[1]-C2.pos[1])**2)<R:
hit_charge=True
if hit_charge or (not (x0<r.y[0] and r.y[0]<x1)) or \
(not (y0<r.y[1] and r.y[1]<y1)):
break
xs.append(x)
ys.append(y)
fig, ax = plt.subplots(figsize = (5,5))
# plot field line
for x, y in zip(xs,ys):
plt.plot(x[:25], y[:25], color="k")
ax.set_xlim(-0.5, 0.5)
ax.set_ylim(-1, 1)
plt.show()
My question is related to # plot field line, xs and ys are generated in the loop before.
The code is composed of classes, a loop that computes xs and ys, and the plotting part follows.
Just print only those lines whose x coords are within the orginal charges x positions plus a certain tolerance eps (due to numerical errors):
eps = 1e-3
for x, y in zip(xs,ys):
if min(x) >= charges[0].pos[0] - eps and max(x) <= charges[1].pos[0] + eps:
plt.plot(x[:25], y[:25], color="k")

Find coordinate on curve

I have plotted curve created by a list with several values. How to find out the x-coordinate that correspond with y-coordinate 0.04400918? This value is not exactly included in the list that describes the curve. Thank you very much.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # 3d graph
from mpl_toolkits.mplot3d import proj3d # 3d graph
import matplotlib.pylab as pl
fig=pl.figure()
ax = Axes3D(fig)
x=[0.02554897, 0.02587839, 0.02623991, 0.02663096, 0.02704882, 0.02749103, 0.02795535, 0.02844018, 0.02894404, 0.02946527, 0.03000235]
y=[0.04739086, 0.0460989, 0.04481555, 0.04354088, 0.04227474, 0.04101689, 0.03976702, 0.03852497, 0.03729052, 0.0360633, 0.03484293]
z=[1.05764017e-18, 1.57788964e-18, 2.00281370e-18, 2.40500994e-18, 2.80239565e-18, 3.19420769e-18, 3.58001701e-18, 3.96024361e-18, 4.33484911e-18, 4.70364652e-18, 5.06672528e-18]
y_point=0.04400918
ax.plot3D(x,y,z)
plt.show()
Here is a specific resolution for your problem.
Some works have already been done for solving line-plane equation. This topic explains how to solve it. Even better, this snippet implements a solution.
For now, we only need to adapt it to our problem.
The first step is to find all the time the line is crossing the plan. To do that, we will iterate over the y dataset and collect all consecutive values when y_point is between them:
lines = []
for i in range(len(y) - 1):
if y[i] >= y_point and y_point >= y[i+1]:
lines.append([[x[i], y[i], z[i]], [x[i+1], y[i+1], z[i+1]]])
Then, for all of these lines, we will solve the intersection equation with the plane. We will use the function provided in sources above.
Finally, we will plot the results
Full code:
# Modules
import numpy as np
import matplotlib.pyplot as plt
# Data
x = [0.02554897, 0.02587839, 0.02623991, 0.02663096, 0.02704882, 0.02749103, 0.02795535, 0.02844018, 0.02894404, 0.02946527, 0.03000235]
y = [0.04739086, 0.0460989, 0.04481555, 0.04354088, 0.04227474, 0.04101689, 0.03976702, 0.03852497, 0.03729052, 0.0360633, 0.03484293]
z = [1.05764017e-18, 1.57788964e-18, 2.00281370e-18, 2.40500994e-18, 2.80239565e-18, 3.19420769e-18, 3.58001701e-18, 3.96024361e-18, 4.33484911e-18, 4.70364652e-18, 5.06672528e-18]
y_point = 0.04400918
# Source: https://rosettacode.org/wiki/Find_the_intersection_of_a_line_with_a_plane#Python
# Resolve intersection
def LinePlaneCollision(planeNormal, planePoint, rayDirection, rayPoint, epsilon=1e-6):
ndotu = planeNormal.dot(rayDirection)
if abs(ndotu) < epsilon:
raise RuntimeError("no intersection or line is within plane")
w = rayPoint - planePoint
si = -planeNormal.dot(w) / ndotu
Psi = w + si * rayDirection + planePoint
return Psi
# For all line, apply the solving process
def solveAllPoints(lines, y_point):
collision_points = []
for line in lines:
# Define plane
planeNormal = np.array([0, 1, 0]) # Plane normal (e.g. y vector)
planePoint = np.array([0, y_point, 0]) # Any point on the plane
# Define ray
rayDirection = line[1] - line[0] # Line direction
rayPoint = line[0] # Any point of the line
# Append point
collision_points.append(LinePlaneCollision(planeNormal, planePoint, rayDirection, rayPoint))
return collision_points
# Find all consecutive Y points crossing the plane.
# This function is only working for the given problem (intersection of the line
# with 1 plan defined by a normal vector = [0,1,0])
def getCrossingLines(y_point, x, y, z):
lines = []
for i in range(len(y) - 1):
if y[i] >= y_point and y_point >= y[i+1]:
lines.append([[x[i], y[i], z[i]], [x[i+1], y[i+1], z[i+1]]])
return np.array(lines)
# Get coordinates for drawing our plane
# Related topic: https://stackoverflow.com/questions/53115276/matplotlib-how-to-draw-a-vertical-plane-in-3d-figure
def getXYZPlane(x, y, z):
xs = np.linspace(min(x), max(x), 100)
zs = np.linspace(min(z), max(z), 100)
X, Z = np.meshgrid(xs, zs)
Y = np.array([y_point for _ in X])
return X, Y, Z
# Create plot
plt3d = plt.figure().gca(projection='3d')
ax = plt.gca()
# Draw data line
ax.plot3D(x,y,z)
# Plot plan
X, Y, Z = getXYZPlane(x, y, z)
ax.plot_surface(X, Y, Z)
# Draw crossing points (lines-planes)
lines = getCrossingLines(y_point, x, y , z)
for pt in solveAllPoints(lines, y_point):
ax.scatter(pt[0], pt[1], pt[2], color='green')
plt.show()
Output

Map plot grid issues

I want to plot a map using Basemap and I can't find how to plot a UTM grid on the map.
I've seen how to plot the grid using long/lat but not in UTM. In Basemap y use epsg=5520 which is UTM 31N.
m = Basemap(epsg=5520, llcrnrlat=52, llcrnrlon=5,urcrnrlat=53, urcrnrlon=6, resolution='l')
m.arcgisimage(server='http://server.arcgisonline.com/arcgis',
service='World_Imagery', xpixels=3500)
m.drawparallels(np.arange(52, 53, 0.05), labels=[1, 0, 0, 0])
m.drawmeridians(np.arange(5, 6, 0.05), labels=[0, 0, 0, 1])
Any thoughts about how to implement a UTM grid?
With Basemap, plotting UTM grid lines or ticks on UTM map projection is not easy because Basemap's data coordinates (conversion from long-lat) are deviated from real UTM values. So, to get appropriate (x,y) from (long, lat), I use pyproj package. In the provided code, command plot() is used to plot all the grid ticks. And annotate() is used to plot the grid labels outside the map area. Values of grid labels need to multiply with 10000 to get metres units.
Here is the working code:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import pyproj
# need pyproj package for coordinate tranformation
pp = pyproj.Proj(init='epsg:5520')
# use map's corners (long,lat) to get grid coordinates (x,y)
corners = [[5,52], [5,53], [6.2, 53], [5.95, 51.5]]
for ea in corners:
x,y = pp(ea[0],ea[1]) #(long,lat) to (x,y)
lon,lat = pp(x, y, inverse=True)
print(x, y, "%4.1f"%(lon), "%4.1f"%(lat))
# the output of print() above, give extents in grid coordinates
#x range: 1630000, 1720000 m -> 1630, 1720 km
#y range: 5710000, 5900000 m -> 5710, 5900 km
low_x, hi_x = 1630000, 1720000
low_y, hi_y = 5710000, 5900000
grid_sp = 10000 # 10km grid spacing
# we will plot grid ticks '+' at 10km spacing
# .. inside the plotting area
lon_lat = [] # for positions of grid ticks '+'
for ea in np.arange(low_x, hi_x, grid_sp): # xs
for eb in np.arange(low_y, hi_y, grid_sp): # ys
lon,lat = pp(ea, eb, inverse=True)
#print(ea, eb, lon, lat)
# lon, lat is good for plotting on basemap
lon_lat.append([lon,lat])
# for annotation above top edge, every 10km
yt = 5870000 # y at top edge of map
xs_top = [] # for labels' positions of x grid
for xi in np.arange(low_x, hi_x, grid_sp): # xs
lon,lat = pp(xi, yt, inverse=True)
#print(ea, eb, lon, lat)
xs_top.append([lon,lat])
# make anno text for every 10 km along map's top edge
anno_top = map(str, list(range(low_x/grid_sp, hi_x/grid_sp)))
# for annotation to the right, every 10km
xr = 1700000 # x at the right edge of map
ys_rt = [] # for labels' positions of y grid
for yi in np.arange(low_y, hi_y, grid_sp): # ys
lon,lat = pp(xr, yi, inverse=True)
#print(xr, yi, lon, lat)
ys_rt.append([lon,lat])
# make anno text for every 10 km along map's right edge
anno_rt = map(str, list(range(low_y/grid_sp, hi_y/grid_sp)))
# prep fig/axes for Basemap plot
fig, ax = plt.subplots(figsize=(10, 12))
m = Basemap(epsg=5520, llcrnrlat=52, llcrnrlon=5, urcrnrlat=53, urcrnrlon=6, resolution='i')
# option to plot imagery, need internet connection
if True:
server = 'http://server.arcgisonline.com/arcgis'
m.arcgisimage(server=server, service='World_Imagery', xpixels=1500)
# plot grid ticks '+' inside map area
m.plot(np.array(lon_lat)[:,0], np.array(lon_lat)[:,1], 'w+', latlon=True, zorder=10)
# option to plot grid labels on top/right edges
if True:
# grid labels on top edge
for id,ea in enumerate(xs_top):
if ea[0]>5.0 and ea[0]<6.0:
ax.annotate(anno_top[id], \
m(ea[0], ea[1]), \
xytext=[-8,50], \
textcoords='offset points', \
color='b')
pass
pass
# grid labels on right edge
for id,ea in enumerate(ys_rt):
if ea[1]>52.0 and ea[1]<53.0:
ax.annotate(anno_rt[id], \
m(ea[0], ea[1]), \
xytext=[10,-5], \
textcoords='offset points', \
color='b')
pass
pass
#m.drawcoastlines(linewidth=0.25)
#m.fillcontinents(color='lightgray')
m.drawparallels(np.arange(52, 53.1, 0.1), labels=[1, 0, 0, 0])
m.drawmeridians(np.arange(5, 6, 0.1), labels=[0, 0, 0, 1])
plt.show()
The resulting plot:

Setting marker size to data in Basemap (Python 3)

Setting markersize to vary for each point (lon, lat) based on related data (crowd) yields the error: float() argument must be a string or a number, not 'list'.
From what I understand about zip, the function should only be calling one number (index) at a time and then iterating to the next. Everything works fine if I set the marker to a constant (e.g. markersize=10).
# 1. get data
lon = [-122.2416355, -122.2977475, -121.204408, -118.3272612, -119.0194639]
lat = [37.7652076, 37.88687, 40.2362738, 33.34221, 35.3738712]
crowd = [8.0, 500.0, 4.0, 44.0, 119.0]
# 2. draw map
map = Basemap(projection='lcc', resolution='h',
lat_0=37.5, lon_0=-119,
width=1E6, height=1.2E6)
map.drawcoastlines()
map.drawcountries()
map.drawstates()
map.fillcontinents()
map.drawmapboundary()
x,y = map(lon, lat)
for x, y, c in zip(lon, lat, crowd):
x,y = map(lon, lat)
map.plot(x, y, 'bo', markersize=crowd)
plt.show()
The working code should be:
import matplotlib.pyplot as plt
import numpy
from mpl_toolkits.basemap import Basemap
# 1. get data
lon = [-122.2416355, -122.2977475, -121.204408, -118.3272612, -119.0194639]
lat = [37.7652076, 37.88687, 40.2362738, 33.34221, 35.3738712]
crowd = [8.0, 500.0, 4.0, 44.0, 119.0]
# 2. draw map
map = Basemap(projection='lcc', resolution='h',
lat_0=37.5, lon_0=-119,
width=1E6, height=1.2E6)
map.drawcoastlines()
map.drawcountries()
map.drawstates()
map.fillcontinents()
map.drawmapboundary()
x,y = map(lon, lat) # convert (long-lat) degrees to map coords
for x1, y1, c in zip(x, y, crowd):
# markersize is scale down by /10
# need alpha<1 to get some transparency
# red color is more appropriate
map.plot(x1, y1, 'ro', markersize=c/10., alpha=0.4)
plt.show()
The resulting plot:

Resources