There is a boundary inside China, which divide the region as North-South. I have drawn this boundary as a polyline format shapefile Download link.
I want to divide those points in the following figures into "North" and "South". Is there any useful function in Python can achieve this.
fiona has point.within function to test points within/out a polygon, but I have not searched a suitable function to divide multiple points by polyline.
Any advices or tips would be appreciated!
updated
According to the valuable suggestion made by Prune, I worked it out. The codes are provided as follows:
from shapely.geometry import shape
from shapely.geometry import LineString
# loading the boundary layer
import fiona
fname = './N-S_boundary.shp'
line1 = fiona.open(fname)
line1 = shape(line1.next()['geometry'])
# set a end point which is the southernmost for all stations.
end_point = (dy[dy['lat']==dy['lat'].min()]['lon'].values[0],dy[dy['lat']==dy['lat'].min()]['lat'].values[0])
# loop all monitoring stations for classification
dy['NS']= np.nan
for i in range(0,len(dy),1):
start_point = (dy['lon'].iloc[i],dy['lat'].iloc[i])
line2 = LineString([start_point, end_point])
if line1.intersection(line2).is_empty:
dy["NS"].iloc[i]='S'
else:
dy["NS"].iloc[i]='N'
color_dict= {'N':'steelblue','S':'r'}
dy['site_color']=dy['NS'].map(color_dict)
You can apply a simple property from topology.
First, make sure that your boundary partitions the universe (all available points you're dealing with). You may need to extend the boundary through the ocean to finish this.
Now, pick any reference point that is labeled as to the region -- to define "North" and "South", you must have at least one such point. w.l.o.g. assume it's a "South" point called Z.
Now, for each point A you want to classify, draw a continuous path (a straight one is usually easiest, but not required) from A to Z. Find the intersections of this path with the boundary. If you have an even quantity of intersections, then A is in the same class ("South") as Z; other wise, it's in the other class ("North").
Note that this requires a topological property of "partition" -- there are no tangents to the boundary line: if your path touches the boundary, it must cross completely.
Related
In this geopandas example, the Antarctica land mass is dropped from the GeoDataFrame before reprojecting to the Mercator projection, in order to prevent problem with shapes containing a pole (which would become infinitely large).
I was wondering, if it is possible to find a more robust reprojection method, so that the dataframe does not need to be manually adjusted. Especially since I'm working with a dataset that does not have a separate row for Antarctica:
I have two ideas:
1. Use information about the destination crs
On the epsg website for example, the 'Area of use' is shown:
We could use it to prepare the data before the reprojection: drop any shapes that extend further South than -80 deg, or alternatively intersect it with a shapely Polygon that describes the area of use of the destination crs, in terms of the source crs - in this case the standard epsg:4326 so Polygon([(-180,-80), (-180,84), ...]).
Issue with this approach: I'm not sure, if this area of use information is programmatically accessible from somewhere for any crs, e.g. from the GeoDataFrame object.
2. Fix in post
Just do it, and pick out the erroneously reprojcted parts later. In my current case, for example, the reprojected geodataframe gdf_merc = gdf.to_crs(epsg=3395) does have errors...
...but by searching for the inf word in the string representation of the geometry, I can find the offending Polygon within the MultiPolygon...
In [360]: for i, polygon in enumerate(gdf_merc.geometry[0]):
...: if 'inf' in str(polygon):
...: print(i)
0
...and just remove it:
Issue with this approach: seems elaborate and I'd prefer to prevent any problems from appearing in the first place.
Any thoughts on how to fix either of these methods, or is there a third way?
One remark: I'm interested in the general case, where any crs could be reprojected to, so I don't want to preemptively remove Antarctica ("just in case"), as other projections might be perfectly fine with it, and, more importantly, they might have other problem areas.
Many thanks!
The option 1 is likely the best shot in here. Latest GeoPandas uses pyproj.CRS to store CRS data, from which you can easily extract bounds of the projection.
To extract it from df:
import geopandas as gpd
df = gpd.read_file(gpd.datasets.get_path('nybb'))
df.crs.area_of_use.bounds
To get it from target CRS using pyproj directly:
import pyproj
crs = pyproj.CRS.from_epsg(3395)
crs.area_of_use.bounds
Then you can use built-in geopandas.clip to clip your data.
from shapely.geometry import box
df = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
crs = pyproj.CRS.from_epsg(3395)
bounds = crs.area_of_use.bounds
clipped = gpd.clip(df, box(*bounds))
clipped.to_crs(crs).plot()
I have a a line segment AB (2d) from point A to point B. For the representation of a coastline (closed polygon, 3*10^3 vertices), I have a NumPy array (2d) of points which start and end at the same point. I want to know, if the connection between point A and B intersects the coastline.
My first approach was to iterate over each line segment of the closed polygon and check if it intersects with AB. Here is the underlying method.
Even if I do this working with NumPy arrays or translating the function with cython, it is not fast enough, because I have to do it a lot of times for different As and Bs.
I thought, this may be a conceptual problem and I was wondering, if there is a smarter way to check only, if at least one intersection exists (True/False)?
I tried to use shapely as well. But this was kind of slow.
from shapely.geometry import LineString
import numpy as np
coastline = LineString(np.dstack(x_values,y_values))
def intersection(A,B,Coastline):
AB = LineString([(A[0], A[1]), (B[0], B[1])])
if AB.intersection(coastline).is_empty:
return False
return True
This is a collision detection problem.
So in your case the best is to put your coastline inside one spatial datastrcture such as bsp-tree, quad-tree, aabb-tree, etc.
Then perform intersection between your line segment and the tree-structure.
See for instance CGAL AABB_tree:
https://doc.cgal.org/latest/AABB_tree/index.html
That library is for 3D but the same idea works for 2D. You can embedd almost any geometry inside aabbtree and query line intersection very fast
I have made a three way venn diagram. I have three issues with it that I can't seem to solve.
What is the code to move the circle labels (i.e."Set1","Set2","Set3") because right now one is too far away from the circle.
What is the code to make the circles be three equal sizes/change the circle size?
What is the code to move the circles around the plot. Right now, set2 is within set3 (but coloured differently), I would like the diagram to look more like the "standard" way of showing a venn diagram (i.e. 3 separate circles with some overlap in the middle).
On another note, I found it difficult to find what the commands such as "set_x", "set_alpha" should be; if anyone knew of a manual that would answer by above questions I would appreciate it, I couldn't seem to find one place with all the information I needed.
import sys
import numpy
import scipy
from matplotlib_venn import venn3,venn3_circles
from matplotlib import pyplot as plt
#Build three lists to make 3 way venn diagram with
list_line = lambda x: set([line.strip() for line in open(sys.argv[x])])
set1,set2,set3 = list_line(1),list_line(2),list_line(3)
#Make venn diagram
vd = venn3([set1,set2,set3],set_labels=("Set1","Set2","Set3"))
#Colours: get the HTML codes from the net
vd.get_patch_by_id("100").set_color("#FF8000")
vd.get_patch_by_id("001").set_color("#5858FA")
vd.get_patch_by_id("011").set_color("#01DF3A")
#Move the numbers in the circles
vd.get_label_by_id("100").set_x(-0.55)
vd.get_label_by_id("011").set_x(0.1)
#Strength of color, 2.0 is very strong.
vd.get_patch_by_id("100").set_alpha(0.8)
vd.get_patch_by_id("001").set_alpha(0.6)
vd.get_patch_by_id("011").set_alpha(0.8)
plt.title("Venn Diagram",fontsize=14)
plt.savefig("output",format="pdf")
What is the code to move the circle labels (i.e."Set1","Set2","Set3") because right now one is too far away from the circle.
Something like that:
lbl = vd.get_label_by_id("A")
x, y = lbl.get_position()
lbl.set_position((x+0.1, y-0.2)) # Or whatever
The "A", "B", and "C" are predefined identifiers, denoting the three sets.
What is the code to make the circles be three equal sizes/change the circle size?
If you do not want the circle/region sizes to correspond to your data (not necessarily a good idea), you can get an unweighted ("classical") Venn diagram using the function venn3_unweighted:
from matplotlib_venn import venn3_unweighted
venn3_unweighted(...same parameters you used in venn3...)
You can further cheat and tune the result by providing a subset_areas parameter to venn3_unweighted - this is a seven-element vector specifying the desired relative size of each region. In this case the diagram will be drawn as if the region areas were subset_areas, yet the numbers will be shown from the actual subsets. Try, for example:
venn3_unweighted(...., subset_areas=(10,1,1,1,1,1,1))
What is the code to move the circles around the plot.
The need to "move the circles around" is somewhat unusual - normally you would either want the circles to be positioned so that their intersection sizes correspond to your data, or use the "default" positioning. The functions venn3 and venn3_unweighted cater to those two requirements. Moving circles around arbitrarily is possible, but would require some lower-level coding and I'd advice against that.
I found it difficult to find what the commands such as "set_x", "set_alpha" should be
The object you get when you call v.get_label_by_id is a Matplotlib Text object. You can read about its methods and properties here. The object returned by v.get_patch_by_id is a PathPatch, look here and here for reference.
I have 2 shapefiles, 1 containing a lot of lines that make up a road network, and another with many GPS points.
So far I've managed to open both shapefiles and do an intersection() using Shapely and Fiona, using the code found here - https://gis.stackexchange.com/a/128210/52590
Here's a copy of my code getting the intersecting points:
from shapely.geometry import shape, MultiLineString
import fiona
Multilines = MultiLineString([shape(line['geometry']) for line in fiona.open("shapefiles/edges.shp")])
Poly = shape(fiona.open("shapefiles/testBuffer.shp").next()['geometry'])
intersecciones = Multilines.intersection(Poly)
And this is what 'intersecciones' looks like when printed:
> MULTILINESTRING ((339395.1489003573 6295646.564306445,
> 339510.1820952367 6295721.782758819), (339391.2927481248 6295686.99659219, 339410.0625 6295699), (339404.4651918385 6295630.405294137, 339520.18020253 6295708.663279793))
So this means there're 3 points of intersection between the lines shapefile and the first polygon of the polygons shapefile.
What I need though is to get two attributes ('Nombre' and 'Sentido') from every line in the lines shapefile that intersects the polygons, in addition to the exact point where they intersect, so I can get the distance from the center of the polygon to the intersecting point after.
So my question is if there's any way to get those attributes, using Shapely or any other Python library there is. Also, what would be the best way to iterate through each polygon and save the data? I'm thinking maybe of a dictionary that contains every polygon with the attributes of the intersecting lines and distance. And last, is there any more efficient way to find the intersections? It's taking around 1 minute to process a single polygon and I'll probably need it to be faster in the future.
If there's any information I'm missing please tell me so I can edit the question.
Thank you very much in advance,
Felipe.
You should have a look at GeoPandas http://geopandas.org/ which uses Fiona and Shapely whilst giving you also direct access to the attributes in a nice tabular format. Together with some pandas operations (such as in this post) you should be able to do what you want with a couple of lines of code.
Probably not the best code, but I solved it by loading the points shapefile (where the points attributes were), the lines shapefile (where the lines attributes were) and polygons (buffered points). Then I used 2 'for' to check if each buffered point intersected each line. If it did, I retrieved the attributes using the same exact 'for'.
In the end I have "listaCalles", which is a list that contains every intersection of polygon with line, with many attributes.
red = fiona.open("shapefiles/miniRedVial.shp") # loads road network
puntos = fiona.open("shapefiles/datosgps.shp") # loads points
# open points with shapely and fiona
Multipoints = MultiPoint([shape(point['geometry']) for point in fiona.open("shapefiles/datosgps.shp")])
# open road network with shapely and fiona
Multilines = MultiLineString([shape(line['geometry']) for line in fiona.open("shapefiles/miniRedVial.shp")])
# open buffered points with shapely and fiona
Polygons = MultiLineString([list(shape(pol['geometry']).exterior.coords) for pol in fiona.open("shapefiles/testBuffer.shp")])
# create list where I'll save the intersections
listaCalles = []
for i in range(0, len(Polygons)):
for j in range(0, len(Multilines)):
if Polygons[i].intersection(Multilines[j]):
idPunto = puntos[i].get("id")
latPunto = puntos[i].get("properties").get("LATITUDE")
lonPunto = puntos[i].get("properties").get("LONGITUDE")
idCalle = red[j].get("id")
nombreCalle = red[j].get("properties").get("NOMBRE")
distPuntoCalle = Multilines[j].distance(Multipoints[i])
listaCalles.append((idPunto, latPunto, lonPunto, idCalle, nombreCalle, distPuntoCalle))
I have gps coordinates. They are just points that create an outline in a few different places, . I want to be able to have a script convert these points into polygons that extend X distance inwards, and also- a way for them to extend both inwards and outwards.
So if I had something like this:
(dots are points)
00000000000000000
00000000000000000
00............000
00.0000000000.000
00.0000000000.000
00.0000000000.000
00.0000000000.000
00.0000000000.000
00............000
00000000000000000
00000000000000000
I could run this program with a distance of 1 and "inwards", and I would end up with a polygon of # shape:
00000000000000000
00000000000000000
00&&&&&&&&&&&&000
00&&&&&&&&&&&&000
00&&00000000&&000
00&&00000000&&000
00&&00000000&&000
00&&&&&&&&&&&&000
00&&&&&&&&&&&&000
00000000000000000
00000000000000000
So far I have tried using circles and then reducing them but it seems wrong / not really feasible. This isn't being performed on a grid, actually it used floats for coordinates.
Any libraries that could do this as well are appreciated.
GDAL/OGR is another option. Ultimately what you want to do is a buffer. To expand your polygons shape outward use a buffer with a positive buffer distance, inwards it would be negative buffer distance. The following is a simple example using a shapefile. Not sure what format your data is in, but I would be surprised if GDAL/OGR can't read it.
import osgeo.ogr
# using ESRI Shape file in this example but there are a number
# of different files this lib supports: http://www.gdal.org/ogr/ogr_formats.html
driver = osgeo.ogr.GetDriverByName('ESRI Shapefile')
osgeo.ogr.UseExceptions()
# Create a data source using the driver...
dataSource = driver.Open("/home/user1/data.shp")
# Get the layer
lyr = dataSource.GetLayer()
# Select the feature in this case using an attribute query
lyr.SetAttributeFilter("column = 'value'")
# verify that you have a feature selected
print 'features in layer:', lyr.GetFeatureCount()
# get the firest feature from the layer
feature = lyr.GetNextFeature()
# get the geometry from the feature
geom = feature.GetGeometryRef()
# perform a 100 unit buffer, not sure what units the coordinates of the
# the data you have are in.
bufferGeom = geom.buffer(100)
# bufferGeom is a geometry object, which is described here:
# <http://cosmicproject.org/OGR/ogr_classes.html#Geometry>
The following is a fantastic resource for getting started with working with spatial data using GDAL/ORG: http://www.gis.usu.edu/~chrisg/python/2009/
Api docs: http://cosmicproject.org/OGR/ogr_classes.html
finally here is the link to the GDAL/OGR page. http://www.gdal.org/
https://pypi.python.org/pypi/Shapely
Shapely is a very good 2d computational geometry library; the way I understand it, it reduces your problem to a single line of code.