(Python) Rtree intersection and fiona questions - python

I'm trying to create a function for an intersection where the input file is of some urban area, and a query box is used to create an output file that has the intersection containing just the buildings found in that query box.
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.basemap import Basemap
import fiona
import fiona.crs
import rtree
input_file = 'se_england_clean.shp'
out_file = 'se_england_out'
file_index = 'Rtree_index_east.idx'
query_box = [-0.0957870483,51.5134165224,-0.08664608,51.5192383994]
def write_clipped_file(name_file_in, out_file, file_index):
idx = rtree.index.Index(file_index)
idx.insert(0, (input_file))
list(idx.intersection((query_box)))[0]
count = 0
with fiona.open(input_file, 'w') as out_file : #?
for building in out_file: #?
No idea if my code is right so far, but I have two immediate problems:
First, I don't know how to open with Fiona the input shapefile and the new (clipped) shapefile that I want to produce in output. I want to cycle the list of indices, select the desired buildings, and write them in the new file 'out_file'. Second, I get an error:
RTreeError: Coordinates must be in the form (minx, miny, maxx, maxy)

idx.insert(0, (input_file))
You need to insert coordinates into the tree, not a file name.

Related

Cartopy: Albers Equal Area projection not working

I am trying to plot a topographic raster in Cartopy. I have downloaded some sample GeoTIFF data from this database: https://zenodo.org/record/3940482. I then import the data and metadata using the Python GDAL library:
from osgeo import gdal
data_object = gdal.Open(path_geotiff)
data_array = data_object.ReadAsArray()
data_transform = data_object.GetGeoTransform()
proj_wkt = data_object.GetProjection()
I need to get the projection as a Cartopy object, which I achieve using the PyProj library for an intermediate step:
from pyproj.crs import CRS
proj_crs = CRS.from_wkt(proj_wkt)
import cartopy.crs as ccrs
proj_cartopy = ccrs.Projection(proj_crs)
This seems to work, and indicates that the projection is one of those supported by Cartopy, Albers Equal Area:
>>> print(proj_cartopy)
PROJCRS["Albers",BASEGEOGCRS["NAD83",DATUM["North American Datum 1983",ELLIPSOID["GRS 1980",6378137,298.257222101004,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4269]],CONVERSION["unnamed",METHOD["Albers Equal Area",ID["EPSG",9822]],PARAMETER["Latitude of false origin",23,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",-106,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",29.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",45.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",0,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",0,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting",east,ORDER[1],LENGTHUNIT["metre",1,ID["EPSG",9001]]],AXIS["northing",north,ORDER[2],LENGTHUNIT["metre",1,ID["EPSG",9001]]]]
I then try to create a GeoAxes object:
import matplotlib.pyplot as plt
subplot_kw = dict(projection = proj_cartopy)
fig, ax = plt.subplots(subplot_kw = subplot_kw)
However, I receive a NotImplemented error:
File "/Users/my_username/opt/anaconda3/envs/carto/lib/python3.9/site-packages/cartopy/mpl/geoaxes.py", line 1598, in _boundary path, = cpatch.geos_to_path(self.projection.boundary)
File "/Users/my_username/opt/anaconda3/envs/carto/lib/python3.9/site-packages/cartopy/crs.py", line 679, in boundary
raise NotImplementedError
Please help me to understand this error (I know I could manually enter the projection, for example using the EPSG code, but I prefer to keep my current automatic method if possible).
From what I understand, this is something that is not fully supported yet.
https://github.com/SciTools/cartopy/issues/813
https://github.com/SciTools/cartopy/issues/153
https://github.com/SciTools/cartopy/issues/923

Convert .geojson to .wkt | extract 'coordinates'

Goal: Ultimately, to convert .geojson to .wkt. Here, I want to extract all coordinates, each as a list.
In my.geojson, there are n many: {"type":"Polygon","coordinates":...
Update: I've successfully extracted the first coordinates. However, this file has two coordinates.
Every .geojson has at least 1 coordinates, but may have more.
How can I dynamically extract all key-values of many coordinates?
Code:
from pathlib import Path
import os
import geojson
import json
from shapely.geometry import shape
ROOT = Path('path/')
all_files = os.listdir(ROOT)
geojson_files = list(filter(lambda f: f.endswith('.geojson'), all_files))
for gjf in geojson_files:
with open(f'{str(ROOT)}/{gjf}') as f:
gj = geojson.load(f)
o = dict(coordinates = gj['features'][0]['geometry']['coordinates'], type = "Polygon")
geom = shape(o)
wkt = geom.wkt
Desired Output:
1 .wkt for all corrdinates in geojson
To convert a series of geometries in GeoJSON files to WKT, the shape() function can convert the GeoJSON geometry to a shapely object which then can be formatted as WKT and/or projected to a different coordinate reference system.
If want to access the coordinates of polygon once it's in a shapely object, usex,y = geo.exterior.xy.
If just want to convert a series of GeoJSON files into one .wkt file per GeoJSON file then try this:
from pathlib import Path
import json
from shapely.geometry import shape
ROOT = Path('path')
for f in ROOT.glob('*.geojson'):
with open(f) as fin, open(f.with_suffix(".wkt"), "w") as fout:
features = json.load(fin)["features"]
for feature in features:
geo = shape(feature["geometry"])
# format geometry coordinates as WKT
wkt = geo.wkt
print(wkt)
fout.write(wkt + "\n")
This output uses your example my.geojson file.
Output:
POLYGON ((19372 2373, 19322 2423, ...
POLYGON ((28108 25855, 27755 26057, ...
If need to convert the coordinates to EPSG:4327 (WGS-84) (e.g. 23.314208, 37.768469), you can use pyproj.
Full code to convert collection of GeoJSON files to a new GeoJSON file in WGS-84.
from pathlib import Path
import json
import geojson
from shapely.geometry import shape, Point
from shapely.ops import transform
from pyproj import Transformer
ROOT = Path('wkt/')
features = []
# assume we're converting from 3857 to 4327
# and center point is at lon=23, lat=37
c = Point(23.676757000000002, 37.9914205)
local_azimuthal_projection = f"+proj=aeqd +R=6371000 +units=m +lat_0={c.y} +lon_0={c.x}"
aeqd_to_wgs84 = Transformer.from_proj(local_azimuthal_projection,
'+proj=longlat +datum=WGS84 +no_defs')
for f in ROOT.glob('*.geojson'):
with open(f) as fin:
features = json.load(fin)["features"]
for feature in features:
geo = shape(feature["geometry"])
poly_wgs84 = transform(aeqd_to_wgs84.transform, geo)
features.append(geojson.Feature(geometry=poly_wgs84))
# Output new GeoJSON file
with open("out.geojson", "w") as fp:
fc = geojson.FeatureCollection(features)
fp.write(geojson.dumps(fc))
Assuming the conversion is from EPSG:3857 to EPSG:4327 and center point is at lon=23, lat=37, the output GeoJSON file will look like this:
{"features": [{"type": "Polygon", "geometry": {"coordinates": [[[23.897879, 38.012554], ...

Clipping a networkx graph according to georeferenced polygon

I am running a loop that computes a networkx.classes.multidigraph.MultiDiGraph for each row (neighbourhood) of a list of GeoDataFrames (cities). It then computes some statistics for each row and writes the file out to disk. The problem is that the loop is extremely long to compute because the graph is computed for each row.
The way I want to quicken the loop is by computing the graph for the whole GeoDataFrame and then clipping the graph into each row (each row has a polygon). You can do this for GeoSeries with geopandas.clip. It seems, however, that no equivalent to geopandas.clip exists for networkx graphs.
Does anyone know of a way to clip a networkx graph?
Alternatively, what other methods exist to speed up my loop.
Note: clipping would work if I could convert the networkx graph to a pandas object. Unfortunately, I think it is not possible to keep the properties which osmnx acts on when the graph is converted to a pandas object. If I'm wrong, please say so.
Here is my initial code:
import osmnx as ox
import pandas as pd
import geopandas as gpd
import os
path="C:/folder/"
files=[os.path.join(path, f) for f in os.listdir(path)]
for i in range(0,2):
city=gpd.read_file(files[i])
circ=[]
for i in range(0,181):
graph_for_row=ox.graph_from_polygon(city.geometry[i])
#above is the long command
stat = ox.basic_stats(graph_for_row)
circ.append(stat['circuity_avg'])
circ=pd.Series(circ)
merged.append(pd.concat([city, circ], axis=1))
for i in (range(0,len(merged))):
with open(geofiles[i], 'w') as f:
f.write(merged[i].to_json())
Here is the new loop I'm aiming for:
clipped_graph=[]
for i in range(0,2):
city=gpd.read_file(files[i])
whole_city=city.unary_union
graph=ox.graph_from_polygon(whole_city)
clipped_graph.append(gpd.clip(graph, city.geometry))#this line
#does not work since 'graph' is a networkx object, not
#a GeoDataFrame or GeoSeries
circ=[]
for i in range(0,181)
stat = ox.basic_stats(clipped_graph[i])
circ.append(stat['circuity_avg'])
circ=pd.Series(circ)
merged.append(pd.concat([city, circ], axis=1))
for i in (range(0,len(merged))):
with open(geofiles[i], 'w') as f:
f.write(merged[i].to_json())
You can use your individual polygons to (spatially) intersect the graph nodes, then use those nodes to induce a subgraph. MWE:
import osmnx as ox
ox.config(use_cache=True, log_console=True)
# load a shapefile of polygons as geodataframe using geopandas
# here i just get 3 cities from OSM to make example reproducible without a shapefile
places = ['Cudahy, CA, USA', 'Bell, CA, USA', 'Maywood, CA, USA']
gdf = ox.gdf_from_places(places)
# get a graph of the union of their boundaries, then extract nodes as geodataframe
G = ox.graph_from_polygon(gdf.unary_union, network_type='drive')
nodes = ox.graph_to_gdfs(G, edges=False)
# for each city polygon, find intersecting nodes then induce subgraph
for polygon in gdf['geometry']:
intersecting_nodes = nodes[nodes.intersects(polygon)].index
G_sub = G.subgraph(intersecting_nodes)
fig, ax = ox.plot_graph(G_sub)

Reading a .VTK polydata file and converting it into Numpy array

I want to convert a .VTK ASCII polydata file into numpy array of just the coordinates of the points. I first tried this: https://stackoverflow.com/a/11894302 but it stores a (3,3) numpy array where each entry is actually the coordinates of THREE points that make that particular cell (in this case a triangle). However, I don't want the cells, I want the coordinates of each point (without repeatition). Next I tried this: https://stackoverflow.com/a/23359921/6619666 with some modifications. Here is my final code. Instead of numpy array, the values are being stored as a tuple but I am not sure if that tuple represents each point.
import sys
import numpy
import vtk
from vtk.util.numpy_support import vtk_to_numpy
reader = vtk.vtkPolyDataReader()
reader.SetFileName('Filename.vtk')
reader.ReadAllScalarsOn()
reader.ReadAllVectorsOn()
reader.Update()
nodes_vtk_array= reader.GetOutput().GetPoints().GetData()
print nodes_vtk_array
Please give suggestions.
You can use dataset_adapter from vtk.numpy_interface:
from vtk.numpy_interface import dataset_adapter as dsa
polydata = reader.GetOutput()
numpy_array_of_points = dsa.WrapDataObject(polydata).Points
From Kitware blog:
It is possible to access PointData, CellData, FieldData, Points
(subclasses of vtkPointSet only), Polygons (vtkPolyData only) this
way.
You can get the point coordinates from a polydata object like so:
polydata = reader.GetOutput()
points = polydata.GetPoints()
array = points.GetData()
numpy_nodes = vtk_to_numpy(array)

Have to point two dimensional graph(x,y) co ordinates networkx python

I am getting information from a spatial database the values are like line string(spatial) format and i need to add this info to networkx graph later need to draw a graph in a matplot lib
I have written this code
cursor.execute("SELECT AsText(roadstring) FROM road1")
for row in cursor.fetchall():
a=row[0][12:-2]
a=str(a)
a=a.split(",")
for i in a:
i=i.split(" ")
i[0]=float(i[0])
i[1]=float(i[1])
weig=abs(i[0]-i[1])
G.add_node((i[0],i[1]))
I unable to get how to add two dimensional edges for roads (x1,y1) to (x2,y2) even i need to add weight to these edges for distance between them
Any suggestions??
The line string of every road are like these 643715.202,2499149.0506 643752.61523545,2499089.86084203 643773.6038,2499056.6558 643773.73878609,2499056.44011079 643793.20162482,2499025.34111554 643813.55943268,2498992.81212045 643826.6563,2498971.8852
I am getting this error I have matplotlib installed I tried by copying your code
Traceback (most recent call last): File "D:\python\gis\new.py", line
2, in
from matplotlib import pyplot as plt File "C:\Python27\lib\site-packages\matplotlib__init__.py", line 133, in
from matplotlib.rcsetup import (defaultParams, File "C:\Python27\lib\site-packages\matplotlib\rcsetup.py", line 19, in
from matplotlib.colors import is_color_like File "C:\Python27\lib\site-packages\matplotlib\colors.py", line 54, in
import matplotlib.cbook as cbook File "C:\Python27\lib\site-packages\matplotlib\cbook.py", line 15, in
import new File "D:\python\gis\new.py", line 2, in
from matplotlib import pyplot as plt File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 20, in
from matplotlib import _pylab_helpers, interactive ImportError: cannot import name interactive
I'm not completely sure what you want to accomplish, but here's how I interpret
it.
You have roads defined as coordinates along the road and you want to draw these
coordinates as nodes and the road between them as edges. You also want the edge
weight to be the distance between the two nodes.
This can quite easily be accomplished by saving the previous node and using the Pythagorean theorem to calculate the distance. This is
how I did it:
import networkx as nx
from matplotlib import pyplot as plt
import math
G = nx.Graph()
row = '643715.202,2499149.0506 643752.61523545,2499089.86084203 ' +\
'643773.6038,2499056.6558 643773.73878609,2499056.44011079 ' +\
'643793.20162482,2499025.34111554 643813.55943268,2498992.81212045 ' +\
'643826.6563,2498971.8852'
a=row.split(" ")
# Saving the previous node to be able to calculate the distance
prev_point = None
# Save the positions in a dictionary to be able to draw
# the nodes at the correct positions
pos = {}
for i in a:
cur_point = tuple([float(x) for x in i.split(',')])
assert len(cur_point) == 2
if prev_point is not None:
# Calculate the distance between the nodes with the Pythagorean
# theorem
b = cur_point[1] - prev_point[1]
c = cur_point[0] - prev_point[0]
a = math.sqrt(b ** 2 + c ** 2)
G.add_edge(cur_point, prev_point, weight=a)
G.add_node(cur_point)
pos[cur_point] = cur_point
prev_point = cur_point
nx.draw(G, pos=pos)
plt.savefig('roads.png')
In this example I assume that a space separates the node positions and each
position's x and y coordinate is comma separated, but this can easily be changed. The above code will output something like this:
This will put the nodes at their "correct" positions, although it can cause some issues if there are big differences in road length. In the above example you can see that two nodes are more or less on top of each other. But that's a different question.

Categories

Resources