python plot a regular grid of points in a polygon - python

I'd like to plot a regular grid of points within a polygon.
Take the following as a polygon:
import geopandas as gpd
from shapely.geometry import Polygon
x = [0, 100, 150, 100, 50]
y = [0, 115, 200, 250, 50]
polygon_geom = Polygon(zip(x, y))
crs = 'epsg:27700'
polygon = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom])
Using that extent I'd like to plot points on a regular grid, the points are separated by a distance of 10 in N-S and E-W.
Then it should only be a matter of using a contains query to keep those inside the polygon.

I modified your points so the polygon is valid by dropping the last set of points:
import geopandas as gpd
from shapely.geometry import Polygon
x = [0, 100, 150, 100]
y = [0, 115, 200, 250]
polygon_geom = Polygon(zip(x, y))
crs = 'epsg:27700'
polygon = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom])
Not sure exactly the grid spec you're looking for, but you could do something along these lines:
import numpy as np, pandas as pd, shapely.vectorized
import matplotlib.pyplot as plt
# define grid spec from min to max value with spacing of grid_res=10
grid_res = 10
# this isn't necessary in your case, but if your polygon had
# boundaries not falling on grid edges, you might want to ensure
# the grid started on specific cell boundaries or midpoints
minx = np.floor(polygon_geom.bounds[0] / grid_res) * grid_res
maxx = np.ceil(polygon_geom.bounds[2] / grid_res) * grid_res
miny = np.floor(polygon_geom.bounds[1] / grid_res) * grid_res
maxy = np.ceil(polygon_geom.bounds[3] / grid_res) * grid_res
minx, maxx, miny, maxy # (0.0, 150.0, 0.0, 250.0)
# you can form the grid by setting up the coordinates and then filling
# the grid with numpy.meshgrid
x = np.arange(minx, maxx + grid_res / 2, grid_res)
y = np.arange(miny, maxy + grid_res / 2, grid_res)
XX, YY = np.meshgrid(x, y)
# use shapely.vectorized to find all points within the polygon
in_polygon = shapely.vectorized.contains(polygon_geom, XX, YY)
# filter the points and flatten them to 1D vectors
x_in_polygon = XX[in_polygon].ravel()
y_in_polygon = YY[in_polygon].ravel()
This can then be plotted like this:
In [41]: fig, ax = plt.subplots()
...: polygon.plot(color='none', edgecolor='k', ax=ax)
...: ax.scatter(x_in_polygon, y_in_polygon, zorder=-1, s=0.4)

Related

Assign line a color by its angle in matplotlib

I'm looking for a way to assign color to line plots in matplotlib in a way that's responsive to the line's angle. This is my current code:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
horz = [[0.5,0.6,0.8],[0.1,0.8,0.9],[0.2,0.5,0.9]]
vert = [[0.1,0.2,0.3],[0.05,0.1,0.15],[0.2,0.3,0.35]]
f = plt.figure(figsize=(6,6))
ax = plt.axes()
for column in range(0,len(horz)):
x = np.array(horz[column])
y = np.array(vert[column])
#LINEAR TRENDLINE
z = np.polyfit(horz[column], vert[column], 1)
p = np.poly1d(z)
ax.plot(horz[column],p(horz[column]),"-")
plt.arrow(x=horz[column][-2],y=p(horz[column])[-2],dx=(horz[column][-1]-horz[column][-2]),dy=(p(horz[column])[-1]-p(horz[column])[-2]), shape='full', lw=.01,
length_includes_head=True, head_width=.012, head_length=0.02, head_starts_at_zero=False, overhang = 0.5)
#FIG SETTINGS
plt.xlim([0, 1])
plt.ylim([0.1,0.5])
ax.set_title('Title',
fontsize = 14)
The idea here would be that if the line is at 0 degrees, it would be at one end of a given gradient, and if it were at 90 degrees, at the other end. Additionally, I'd like the line length to be taken as the intensity of the color. So if the line is short, it'd be closer to white, and if the line is long, it'd be closer to the raw color from the gradient.
Managed to solve it myself. Used pretty simple formulas for calculating the lines' slopes and distances and then used these as input for the color mapping and alpha transparency attribute.
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors as colors
import numpy as np
%matplotlib inline
#Data
horz = [[0.5,0.6,0.8],[0.1,0.3,0.4],[0.2,0.5,0.9],[0.9,0.95,0.95]]
vert = [[0.1,0.2,0.45],[0.05,0.1,0.15],[0.2,0.3,0.35],[0.1,0.3,0.5]]
#Slope calculation
def slopee(x1,y1,x2,y2):
x = (y2 - y1) / (x2 - x1)
return x
#Color set up
cmap = plt.cm.coolwarm_r
#0 means a horizontal line, 1 means a line at 45 degrees, infinite means a vertical line (2 is vertical enough)
cNorm = colors.Normalize(vmin=0, vmax=2)
scalarMap = cm.ScalarMappable(norm=cNorm,cmap=cmap)
#Fig settings
f = plt.figure(figsize=(6,6))
ax = plt.axes()
for column in range(0,len(horz)):
x = np.array(horz[column])
y = np.array(vert[column])
#LINEAR TRENDLINE
# 1 LINEAR
# >=2 POLINOMIAL
z = np.polyfit(horz[column], vert[column], 1)
p = np.poly1d(z)
#Distance calc formula
def calculateDistance(x1,y1,x2,y2):
dist = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
return dist
#Set up max an min distances
maxdist = calculateDistance(0,0,0,0.9)
mindist = calculateDistance(0,0,0,0)
#Calculate line slope
slope = slopee(horz[column][0],p(horz[column])[0],horz[column][-1],p(horz[column])[-1])
#Not interested in any slopes going "down"
if slope >=0:
#Map colors based on slope (0-2)
colorVal = scalarMap.to_rgba(slope)
#Map transparency based on distance
transparency = (calculateDistance(horz[column][0],p(horz[column])[0],horz[column][-1],p(horz[column])[-1])-mindist)/(maxdist-mindist)
#Set up minimun transparency to be 50% instead of 0%
transparency = (0.5*transparency) + 0.5
#The actual arrow plot
plt.arrow(x=horz[column][0],y=p(horz[column])[0],dx=(horz[column][-1]-horz[column][0]),dy=(p(horz[column])[-1]-p(horz[column])[0]), shape='full',length_includes_head=True, head_starts_at_zero=False, lw=.5, head_width=.011, head_length=0.01, overhang = 0.5, color=colorVal,alpha=transparency)
#FIG SETTINGS
plt.xlim([0, 1])
plt.ylim([0,0.5])
ax.set_title('Title',fontsize = 14)
Congrats on solving it yourself. I had put this together before I realized you had posted your answer. Very similar approach:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm, colors
from math import sqrt
plt.rcParams["figure.figsize"] = (15,15)
# Create a color mapper for degress to color
values = np.linspace(1.0, 90.0, 90)
norm = colors.Normalize(vmin=0.0, vmax=90.0, clip=True)
mapper = cm.ScalarMappable(norm=norm, cmap=cm.coolwarm_r)
horz = [[0.5,0.6,0.8],[0.1,0.3,0.4],[0.2,0.5,0.9],[0.9,0.95,0.95]]
vert = [[0.1,0.2,0.45],[0.05,0.1,0.15],[0.2,0.3,0.35],[0.1,0.3,0.5]]
f = plt.figure(figsize=(15,15))
ax = plt.axes()
# Calculate lengths of each line
lengths = [sqrt((x[-1]-x[0])**2 + (y[-1]-y[0])**2) for x,y in zip(horz, vert)]
for x,y,length in zip(horz,vert, lengths):
alpha = length / sum(lengths)
angle = np.rad2deg(np.arctan2(y[-1] - y[0], x[-1] - x[0]))
color = mapper.to_rgba(angle)
plt.arrow(x[0],y[0],x[-1]-x[0], y[-1]-y[0],shape='full', lw=2,
length_includes_head=True, head_width=.012, head_length=0.02, head_starts_at_zero=False, overhang = 0.5, alpha=alpha, color=color)

How to combine two outputs of KernelDensity from sklearn (Python)

I want to multiply two Kernel Density Estimates to get a composite likelihood ... The multiplication operator doesn't exist for sklearn.
I have KDR for data from 3 independent sources and I want multiply KDE from each sources. The below code is from https://scikit-learn.org/stable/auto_examples/neighbors/plot_species_kde.html#sphx-glr-auto-examples-neighbors-plot-species-kde-py and should run easily.
Although I made some small modification to get get two KDEs and then multiply them which fails.
Someone can help. Below code should run without error except the multiplication part.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_species_distributions
from sklearn.neighbors import KernelDensity
# if basemap is available, we'll use it.
# otherwise, we'll improvise later...
try:
from mpl_toolkits.basemap import Basemap
basemap = True
except ImportError:
basemap = False
def construct_grids(batch):
"""Construct the map grid from the batch object
Parameters
----------
batch : Batch object
The object returned by :func:`fetch_species_distributions`
Returns
-------
(xgrid, ygrid) : 1-D arrays
The grid corresponding to the values in batch.coverages
"""
# x,y coordinates for corner cells
xmin = batch.x_left_lower_corner + batch.grid_size
xmax = xmin + (batch.Nx * batch.grid_size)
ymin = batch.y_left_lower_corner + batch.grid_size
ymax = ymin + (batch.Ny * batch.grid_size)
# x coordinates of the grid cells
xgrid = np.arange(xmin, xmax, batch.grid_size)
# y coordinates of the grid cells
ygrid = np.arange(ymin, ymax, batch.grid_size)
return (xgrid, ygrid)
# Get matrices/arrays of species IDs and locations
data = fetch_species_distributions()
species_names = ['Bradypus Variegatus', 'Microryzomys Minutus']
Xtrain = np.vstack([data['train']['dd lat'], data['train']['dd long']]).T
ytrain = np.array([d.decode('ascii').startswith('micro')
for d in data['train']['species']], dtype='int')
Xtrain *= np.pi / 180. # Convert lat/long to radians
# Set up the data grid for the contour plot
xgrid, ygrid = construct_grids(data)
X, Y = np.meshgrid(xgrid[::5], ygrid[::5][::-1])
land_reference = data.coverages[6][::5, ::5]
land_mask = (land_reference > -9999).ravel()
xy = np.vstack([Y.ravel(), X.ravel()]).T
xy = xy[land_mask]
xy *= np.pi / 180.
# Plot map of South America with distributions of each species
fig = plt.figure()
fig.subplots_adjust(left=0.05, right=0.95, wspace=0.05)
kde0 = KernelDensity(bandwidth=0.04, metric='haversine',
kernel='gaussian', algorithm='ball_tree')
kde0.fit(Xtrain[ytrain == 0])
kde1 = KernelDensity(bandwidth=0.04, metric='haversine',
kernel='gaussian', algorithm='ball_tree')
kde1.fit(Xtrain[ytrain == 1])
kde01=kde0*kde1
plt.subplot(1, 1, 1)
# evaluate only on the land: -9999 indicates ocean
Z = np.full(land_mask.shape[0], -9999, dtype='int')
Z[land_mask] = np.exp(kde01.score_samples(xy))
Z = Z.reshape(X.shape)
# plot contours of the density
levels = np.linspace(0, Z.max(), 25)
plt.contourf(X, Y, Z, levels=levels, cmap=plt.cm.Reds)
plt.show()

Creating a filled numpy array of ones from a polygon points Python

I would be very gratefull if anyone could help me find a faster solution to my problem.
Heres the scenario:- I have a polygon of float points which I want to map to a grid. The grid cells can be different width and height not uniformed like my image shows. ie rectangular.
I have tried using Image draw but it only uses ints. Converting floats to ints means I have to scale the floats up and remove decimal to keep some precision but image draw will not work with the larger polygon of points.
Is there a more eloquent and fast way of achieving a numpy array of ones (blue) for the filled area of the polygon and zeros (red) for the rest. I have read a little on mesh grid but cant see how it can be of use for this scenario.
Many thanks
The results from the code are
cols = 4
rows = 4
points = [[1535116L, 1725047L], [1535116L, 2125046L], [-464884L, 2125046L], [-464884L, 125046L]]
bbCut = getPythonBoundBox(points)
cutWidth = bbCut[1][0]-bbCut[0][0]
scale = float(cutWidth) / float(rows)
###Center data to origin
for p in range(len(points)):
points[p][0] -= (bbCut[1][0] - bbCut[0][0])/2
points[p][1] -= (bbCut[1][1] - bbCut[0][1])/2
points[p][0] /= scale
points[p][1] /= scale
##move points to Zero
bbCut = getPythonBoundBox(points)
for p in range(len(points)):
points[p][0] -=bbCut[0][0]
points[p][1] -=bbCut[0][1]
pointToTuple= []
for p in range(len(points)):
pointToTuple.append((points[p][0], points[p][1]))
imgWidth = float(rows)
imgHeight = float(cols)
img = Image.new('L', (int(imgWidth), int(imgHeight)), 0)
draw = ImageDraw.Draw(img)
draw.polygon(pointToTuple, fill=1)
array = np.reshape(list(img.getdata()), (cols, rows))
############This is the result from the array############
##If you compare this array to the coloured scaled image ive have drawn
##its missing a 1 on the second value in the first row
##and another 1 on the second row 3rd value
##I'm assuming there is some parsing happening here with float to int?
array([1, 0, 0, 0])
array([1, 1, 0, 0])
array([1, 1, 1, 1])
array([1, 1, 1, 1])
#########################################################
def getPythonBoundBox(points):
bigNumber = 10e10
xmin = bigNumber
xmax = -bigNumber
ymin = bigNumber
ymax = -bigNumber
g = []
a = len(points)
for i in xrange(a):
if points[i][0] < xmin: xmin = points[i][0]
if points[i][0] > xmax: xmax = points[i][0]
if points[i][1] < ymin: ymin = points[i][1]
if points[i][1] > ymax: ymax = points[i][1]
p1 = [xmin,ymin]
g.append(p1)
p2 = [xmax,ymax]
g.append(p2)
return (g)
matplotlib.path.Path has a method contains_points. Hence simply instantiate a path with your polygon points and then check your grid coordinates if they fall within that path. Your grid can have any resolution you want. This is controlled by nx and ny (or alternatively dx and dy) in the code below.
Code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.path import Path
# create a matplotlib path
points = [[1535116L, 1725047L],
[1535116L, 2125046L],
[-464884L, 2125046L],
[-464884L, 125046L],
[1535116L, 1725047L]]
codes = [Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
]
path = Path(points, codes)
# check the path
fig, (ax1, ax2, ax3) = plt.subplots(1,3)
patch = PathPatch(path, facecolor='k')
ax1.add_patch(patch)
xmin, ymin = np.min(points, axis=0)
xmax, ymax = np.max(points, axis=0)
ax1.set_ylim(ymin,ymax)
ax1.set_xlim(xmin,xmax)
ax1.set_aspect('equal')
# create a grid
nx, ny = 1000, 1000
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
xgrid, ygrid = np.meshgrid(x, y)
pixel_coordinates = np.c_[xgrid.ravel(), ygrid.ravel()]
# find points within path
img = path.contains_points(pixel_coordinates).reshape(nx,ny)
# plot
ax2.imshow(img, cmap='gray_r', interpolation='none', origin='lower')
# repeat, but this time specify pixel widths explicitly
dx, dy = 2000, 2000
x = np.arange(xmin, xmax, dx)
y = np.arange(ymin, ymax, dy)
xgrid, ygrid = np.meshgrid(x, y)
pixel_coordinates = np.c_[xgrid.ravel(), ygrid.ravel()]
img = path.contains_points(pixel_coordinates).reshape(len(x), len(y))
ax3.imshow(img, cmap='gray_r', interpolation='none', origin='lower')
UPDATE:
Ok, so this now tests the if any of the corners of each tile are within the path. For some reason, I still get another answer than the picture suggests. How sure are you, that the points that you provide are exact?
Code + image:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.path import Path
# create a matplotlib path
points = [[1535116L, 1725047L],
[1535116L, 2125046L],
[-464884L, 2125046L],
[-464884L, 125046L],
[1535116L, 1725047L]]
codes = [Path.MOVETO,
Path.LINETO,
Path.LINETO,
Path.LINETO,
Path.CLOSEPOLY,
]
path = Path(points, codes)
fig, (ax1, ax2) = plt.subplots(1,2)
patch = PathPatch(path, facecolor='k')
ax1.add_patch(patch)
xmin, ymin = np.min(points, axis=0)
xmax, ymax = np.max(points, axis=0)
ax1.set_ylim(ymin,ymax)
ax1.set_xlim(xmin,xmax)
ax1.set_aspect('equal')
nx, ny = 4, 4
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
xgrid, ygrid = np.meshgrid(x, y)
pixel_centers = np.c_[xgrid.ravel(), ygrid.ravel()]
def pixel_center_to_corners(x, y, dx, dy, precision=0.):
"""
Returns array indexed by (pixel, corner, (x,y))
"""
# make dx and dy ever so slightly smaller,
# such that the points fall **inside** the path (not **on** the path)
dx -= precision
dy -= precision
return np.array([(x - dx/2., y - dy/2.), # lower left
(x + dx/2., y - dy/2.), # lower right
(x + dx/2., y + dy/2.), # upper right
(x - dx/2., y + dy/2.), # upper left
]).transpose([2,0,1])
# get pixel corners
dx = (xmax - xmin) / float(nx)
dy = (ymax - ymin) / float(ny)
pixel_corners = pixel_center_to_corners(pixel_centers[:,0], pixel_centers[:,1], dx, dy)
# test corners of each pixel;
# set img to True, iff any corners within path;
img = np.zeros((len(pixel_corners)))
for ii, pixel in enumerate(pixel_corners):
is_inside_path = path.contains_points(pixel)
img[ii] = np.any(is_inside_path)
img = img.reshape(len(x), len(y))
ax2.imshow(img, cmap='gray_r', interpolation='none', origin='lower')

Contour plot data (lat,lon,value) within boundaries and export GeoJSON

I'm trying to interpolate data within boundaries and plot contour lines (polygons) based on Latitude, longitude, value data from csv files.
Results I want print as geojson.
I'm stuck on the basic contour plot from csv. I really appreciate help here.
This is what I got in this moment but can't make it work.
import numpy as np
import matplotlib.pyplot as plt
data = np.genfromtxt('temp.csv', delimiter=',')
x = data[:1]
y = data[:2]
[x,y] = meshgrid(x,y);
z = data[:3];
plt.contour(x,y,z)
plt.show()
The CSV looks like this
2015-10-28T09:25:12.800Z,51.56325,17.529043333,4.6484375,19.8046875
2015-10-28T09:25:12.900Z,51.56325,17.529041667,4.4921875,19.6875
2015-10-28T09:25:13.000Z,51.563248333,17.529041667,4.453125,19.9609375
2015-10-28T09:25:13.200Z,51.563245,17.529041667,4.140625,19.765625
2015-10-28T09:25:13.300Z,51.563243333,17.529041667,4.609375,19.6484375
2015-10-28T09:25:13.500Z,51.563241667,17.529041667,4.609375,19.53125
2015-10-28T09:25:13.600Z,51.56324,17.529041667,4.4921875,19.375
2015-10-28T09:25:13.700Z,51.563238333,17.529041667,4.4140625,19.765625
2015-10-28T09:25:13.800Z,51.563236667,17.529041667,4.453125,20.234375
2015-10-28T09:25:13.900Z,51.563235,17.529041667,4.3359375,19.84375
2015-10-28T09:25:14.000Z,51.563233333,17.529041667,4.53125,19.453125
2015-10-28T09:25:14.100Z,51.563231667,17.529041667,4.53125,19.8046875
2015-10-28T09:25:14.200Z,51.563228333,17.529041667,4.1796875,19.4921875
2015-10-28T09:25:14.300Z,51.563226667,17.529041667,4.2578125,19.453125
2015-10-28T09:25:14.400Z,51.563225,17.529041667,4.4921875,19.4921875
2015-10-28T09:25:14.500Z,51.563223333,17.529041667,4.375,19.453125
2015-10-28T09:25:14.600Z,51.563221667,17.529041667,4.609375,18.90625
2015-10-28T09:25:14.700Z,51.563218333,17.529041667,4.53125,19.6875
2015-10-28T09:25:14.900Z,51.563215,17.529041667,4.140625,18.75
2015-10-28T09:25:15.000Z,51.563213333,17.529041667,4.453125,18.828125
Column 1 - Latitude
Column 2 - Longitude
Column 3 - Value
For contour lines I need also limits - for example (4.1,4.3,4.6)
I guess there is some mistake in your code (according to your data you shouldn't do x = data[:1] but more x = data[..., 1]).
With your of data, the basic steps I will follow to interpolate the z value and fetch an output as a geojson would require at least the shapely module (and here geopandas is used for the convenience).
import numpy as np
import matplotlib.pyplot as plt
from geopandas import GeoDataFrame
from matplotlib.mlab import griddata
from shapely.geometry import Polygon, MultiPolygon
def collec_to_gdf(collec_poly):
"""Transform a `matplotlib.contour.QuadContourSet` to a GeoDataFrame"""
polygons, colors = [], []
for i, polygon in enumerate(collec_poly.collections):
mpoly = []
for path in polygon.get_paths():
try:
path.should_simplify = False
poly = path.to_polygons()
# Each polygon should contain an exterior ring + maybe hole(s):
exterior, holes = [], []
if len(poly) > 0 and len(poly[0]) > 3:
# The first of the list is the exterior ring :
exterior = poly[0]
# Other(s) are hole(s):
if len(poly) > 1:
holes = [h for h in poly[1:] if len(h) > 3]
mpoly.append(Polygon(exterior, holes))
except:
print('Warning: Geometry error when making polygon #{}'
.format(i))
if len(mpoly) > 1:
mpoly = MultiPolygon(mpoly)
polygons.append(mpoly)
colors.append(polygon.get_facecolor().tolist()[0])
elif len(mpoly) == 1:
polygons.append(mpoly[0])
colors.append(polygon.get_facecolor().tolist()[0])
return GeoDataFrame(
geometry=polygons,
data={'RGBA': colors},
crs={'init': 'epsg:4326'})
data = np.genfromtxt('temp.csv', delimiter=',')
x = data[..., 1]
y = data[..., 2]
z = data[..., 3]
xi = np.linspace(x.min(), x.max(), 200)
yi = np.linspace(y.min(), y.max(), 200)
zi = griddata(x, y, z, xi, yi, interp='linear') # You could also take a look to scipy.interpolate.griddata
nb_class = 15 # Set the number of class for contour creation
# The class can also be defined by their limits like [0, 122, 333]
collec_poly = plt.contourf(
xi, yi, zi, nb_class, vmax=abs(zi).max(), vmin=-abs(zi).max())
gdf = collec_to_gdf(collec_poly)
gdf.to_json()
# Output your collection of features as a GeoJSON:
# '{"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[51.563214073747474,
# (...)
Edit:
You can grab the colors values (as an array of 4 values in range 0-1, representing RGBA values) used by matplotplib by fetching them on each item of the collection with the get_facecolor() method (and then use them to populate one (or 4) column of your GeoDataFrame :
colors = [p.get_facecolor().tolist()[0] for p in collec_poly.collections]
gdf['RGBA'] = colors
Once you have you GeoDataFrame you can easily get it intersected with your boundaries. Use these boundaries to make a Polygon with shapely and compute the intersection with your result:
mask = Polygon([(51,17), (51,18), (52,18), (52,17), (51,17)])
gdf.geometry = gdf.geometry.intersection(mask)
Or read your geojson as a GeoDataFrame:
from shapely.ops import unary_union, polygonize
boundary = GeoDataFrame.from_file('your_geojson')
# Assuming you have a boundary as linestring, transform it to a Polygon:
mask_geom = unary_union([i for i in polygonize(boundary.geometry)])
# Then compute the intersection:
gdf.geometry = gdf.geometry.intersection(mask_geom)
Almost I got what I excepted. Based on mgc answer. I change griddata as you suggest to scipy.interpolate.griddata and interpolation method to nearest. Now its works like I want.
Only last thing that I need is to limit this interpolation to polygon (boundaries) also from geoJson.
The other problem is to export to geojson polygons WITH colors as attributes.
import numpy as np
import matplotlib.pyplot as plt
#from matplotlib.mlab import griddata
from scipy.interpolate import griddata
from geopandas import GeoDataFrame
from shapely.geometry import Polygon, MultiPolygon
def collec_to_gdf(collec_poly):
"""Transform a `matplotlib.contour.QuadContourSet` to a GeoDataFrame"""
polygons = []
for i, polygon in enumerate(collec_poly.collections):
mpoly = []
for path in polygon.get_paths():
try:
path.should_simplify = False
poly = path.to_polygons()
# Each polygon should contain an exterior ring + maybe hole(s):
exterior, holes = [], []
if len(poly) > 0 and len(poly[0]) > 3:
# The first of the list is the exterior ring :
exterior = poly[0]
# Other(s) are hole(s):
if len(poly) > 1:
holes = [h for h in poly[1:] if len(h) > 3]
mpoly.append(Polygon(exterior, holes))
except:
print('Warning: Geometry error when making polygon #{}'
.format(i))
if len(mpoly) > 1:
mpoly = MultiPolygon(mpoly)
polygons.append(mpoly)
elif len(mpoly) == 1:
polygons.append(mpoly[0])
return GeoDataFrame(geometry=polygons, crs={'init': 'epsg:4326'})
data = np.genfromtxt('temp2.csv', delimiter=',')
x = data[..., 1]
y = data[..., 2]
z = data[..., 4]
xi = np.linspace(x.min(), x.max(), num=100)
yi = np.linspace(y.min(), y.max(), num=100)
#zi = griddata(x, y, z, xi, yi, interp='nn') # You could also take a look to scipy.interpolate.griddata
zi = griddata((x, y), z, (xi[None,:], yi[:,None]), method='nearest')
nb_class = [5,10,15,20,25,30,35,40,45,50] # Set the number of class for contour creation
# The class can also be defined by their limits like [0, 122, 333]
collec_poly = plt.contourf(
xi, yi, zi, nb_class, vmax=abs(zi).max(), vmin=-abs(zi).max())
gdf = collec_to_gdf(collec_poly)
#gdf.to_json()
print gdf.to_json()
plt.plot(x,y)
plt.show()

Drawing ellipses on matplotlib basemap projections

I am trying to draw ellipses on a basemap projection. To draw a circle like polygon there is the tissot function used to draw Tissot's indicatrix' as illustrates the following example.
from mpl_toolkits.basemap import Basemap
x0, y0 = 35, -50
R = 5
m = Basemap(width=8000000,height=7000000, resolution='l',projection='aea',
lat_1=-40.,lat_2=-60,lon_0=35,lat_0=-50)
m.drawcoastlines()
m.tissot(x0, y0, R, 100, facecolor='g', alpha=0.5)
However, I am interested in plotting an ellipsis in the form (x-x0)**2/a**2 + (y-y0)**2/2 = 1. On the other hand, to draw an ellipsis on a regular Cartesian grid I can use the following sample code:
import pylab
from matplotlib.patches import Ellipse
fig = pylab.figure()
ax = pylab.subplot(1, 1, 1, aspect='equal')
x0, y0 = 35, -50
w, h = 10, 5
e = Ellipse(xy=(x0, y0), width=w, height=h, linewidth=2.0, color='g')
ax.add_artist(e)
e.set_clip_box(ax.bbox)
e.set_alpha(0.7)
pylab.xlim([20, 50])
pylab.ylim([-65, -35])
Is there a way to plot an ellipsis on a basemap projection with the an effect similar to tissot?
After hours analyzing the source code of basemap's tissot function, learning some properties of ellipses and lot's of debugging, I came with a solution to my problem. I've extended the basemap class with a new function called ellipse as follows,
from __future__ import division
import pylab
import numpy
from matplotlib.patches import Polygon
from mpl_toolkits.basemap import pyproj
from mpl_toolkits.basemap import Basemap
class Basemap(Basemap):
def ellipse(self, x0, y0, a, b, n, ax=None, **kwargs):
"""
Draws a polygon centered at ``x0, y0``. The polygon approximates an
ellipse on the surface of the Earth with semi-major-axis ``a`` and
semi-minor axis ``b`` degrees longitude and latitude, made up of
``n`` vertices.
For a description of the properties of ellipsis, please refer to [1].
The polygon is based upon code written do plot Tissot's indicatrix
found on the matplotlib mailing list at [2].
Extra keyword ``ax`` can be used to override the default axis instance.
Other \**kwargs passed on to matplotlib.patches.Polygon
RETURNS
poly : a maptplotlib.patches.Polygon object.
REFERENCES
[1] : http://en.wikipedia.org/wiki/Ellipse
"""
ax = kwargs.pop('ax', None) or self._check_ax()
g = pyproj.Geod(a=self.rmajor, b=self.rminor)
# Gets forward and back azimuths, plus distances between initial
# points (x0, y0)
azf, azb, dist = g.inv([x0, x0], [y0, y0], [x0+a, x0], [y0, y0+b])
tsid = dist[0] * dist[1] # a * b
# Initializes list of segments, calculates \del azimuth, and goes on
# for every vertex
seg = [self(x0+a, y0)]
AZ = numpy.linspace(azf[0], 360. + azf[0], n)
for i, az in enumerate(AZ):
# Skips segments along equator (Geod can't handle equatorial arcs).
if numpy.allclose(0., y0) and (numpy.allclose(90., az) or
numpy.allclose(270., az)):
continue
# In polar coordinates, with the origin at the center of the
# ellipse and with the angular coordinate ``az`` measured from the
# major axis, the ellipse's equation is [1]:
#
# a * b
# r(az) = ------------------------------------------
# ((b * cos(az))**2 + (a * sin(az))**2)**0.5
#
# Azymuth angle in radial coordinates and corrected for reference
# angle.
azr = 2. * numpy.pi / 360. * (az + 90.)
A = dist[0] * numpy.sin(azr)
B = dist[1] * numpy.cos(azr)
r = tsid / (B**2. + A**2.)**0.5
lon, lat, azb = g.fwd(x0, y0, az, r)
x, y = self(lon, lat)
# Add segment if it is in the map projection region.
if x < 1e20 and y < 1e20:
seg.append((x, y))
poly = Polygon(seg, **kwargs)
ax.add_patch(poly)
# Set axes limits to fit map region.
self.set_axes_limits(ax=ax)
return poly
This new function can be used promptly like in this example:
pylab.close('all')
pylab.ion()
m = Basemap(width=12000000, height=8000000, resolution='l', projection='stere',
lat_ts=50, lat_0=50, lon_0=-107.)
m.drawcoastlines()
m.fillcontinents(color='coral',lake_color='aqua')
# draw parallels and meridians.
m.drawparallels(numpy.arange(-80.,81.,20.))
m.drawmeridians(numpy.arange(-180.,181.,20.))
m.drawmapboundary(fill_color='aqua')
# draw ellipses
ax = pylab.gca()
for y in numpy.linspace(m.ymax/20, 19*m.ymax/20, 9):
for x in numpy.linspace(m.xmax/20, 19*m.xmax/20, 12):
lon, lat = m(x, y, inverse=True)
poly = m.ellipse(lon, lat, 3, 1.5, 100, facecolor='green', zorder=10,
alpha=0.5)
pylab.title("Ellipses on stereographic projection")
Which has the following outcome:

Categories

Resources