Changing long, lat values of Polygon coordinates in python - python-3.x

I have a basic shape file of all the us states which can be found here..
shapefile
I am looking to edit the positions of the 2 states hawaii and alaska, i wish to change the coordinates of the state of hawaii so that it roughly sits under the state of nevada, and i also with to change the state of alaska so that is is considerably smaller.. and also so it sits roughly below both the state of california and Arizona, il include an image just so theres a visual of my idea..
as you can see alaska and hawaii are sitting on the bottom left of the large us mainland just under the states mentioned before.
I know for this to happen i need to change the longitude and latitude coordinates of both states using geopandas etc.
So i started off with the state of hawaii and began accessing the polygons coordinates using numpy.
here is a snippet of the code so far
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point
import matplotlib.pyplot as plt
from shapely.geometry import Polygon
from shapely.geometry import Point, Polygon
import numpy as np
poly_States = gpd.read_file("states.shp")
hawaii = poly_States[poly_States.STATE_ABBR == "HI"]
coords = [i for i in hawaii.geometry]
all_Coords = []
for b in coords[0].boundary:
coords = np.dstack(b.coords.xy).tolist()
all_Coords.append(*coords)
for cord_1 in all_Coords:
for cord2 in cord_1:
cord2[0] = cord2[0] + 54.00000000000000
my idea here was to access the coordinates in array format and change the latitude coordinates by adding 54, so basically shifting the entire state to the right to have it sitting rougly under new mexico.
my issue lies in actually returning theses changes to the polygon object in the shapefile itself.
i feel like there is probably an easier method maybe by accessing attributes of the polygon or using some sort of external software, but i believe that if im able to properly access the long,lat values and change them i should be able to make the changes in positioning and size that i need.
Thanks in advance.

You can use translate and assign the new geometry like this:
m = poly_States.STATE_ABBR == "HI"
poly_States[m] = poly_States[m].set_geometry(poly_States[m].translate(54))
Result:
The same way you can scale and shift Alaska:
m = poly_States.STATE_ABBR == "AK"
poly_States[m] = poly_States[m].set_geometry(poly_States[m].scale(.2,.2,.2).translate(40, -40))

Related

OSMNx : get coordinates of nodes/corners/edges of polygons/buildings

I am trying to retrieve the coordinates of all nodes/corners/edges of each commercial building in a list. E.g. for the supermarket Aldi in Macclesfield (UK), I can get from the UI 10 nodes (all the corners/edges of the supermarket) but I can only retrieve from osmnx 2 of those 10 nodes. I would need to access to the complete list of nodes but it truncates the results giving only 2 nodes of 10 in this case.Using this code below:
import osmnx as ox
test = ox.geocode_to_gdf('aldi, Macclesfield, Cheshire, GB')
ax = ox.project_gdf(test).plot()
test.geometry
or
gdf = ox.geometries_from_place('Grosvenor, Macclesfield, Cheshire, GB', tags)
gdf.geometry
Both return just two coordinates and truncate other info/results that is available in openStreetMap UI (you can see it in the first column of the image attached geometry>POLYGON>only two coordinates and other results truncated...). I would appreciate some help on this, thanks in advance.
It's hard to guess what you're doing here because you didn't provide a reproducible example (e.g., tags is undefined). But I'll try to guess what you're going for.
I am trying to retrieve the coordinates of all nodes/corners/edges of commercial buildings
Here I retrieve all the tagged commercial building footprints in Macclesfield, then extract the first one's polygon coordinates. You could instead filter these by other attribute values as you see fit if you only want certain kinds of buildings. Proper usage of OSMnx's geometries module is described in the documentation.
import osmnx as ox
# get the building footprints in Macclesfield
place = 'Macclesfield, Cheshire, England, UK'
tags = {'building': 'commercial'}
gdf = ox.geometries_from_place(place, tags)
# how many did we get?
print(gdf.shape) # (57, 10)
# extract the coordinates for the first building's footprint
gdf.iloc[0]['geometry'].exterior.coords
Alternatively, if you want a specific building's footprint, you can look up its OSM ID and tell OSMnx to geocode that value:
gdf = ox.geocode_to_gdf('W251154408', by_osmid=True)
polygon = gdf.iloc[0]['geometry']
polygon.exterior.coords
gdf = ox.geocode_to_gdf('W352332709', by_osmid=True)
polygon = gdf.iloc[0]['geometry']
polygon.exterior.coords
list(polygon.exterior.coords)

OSMNX graph_from_gdfs KeyError: 'x' when converting a Geopackage to a Graph

I need to edit data downloaded by osmnx in geopackage format and then import it as a graph for calculating distances, isochrones etc.
Current Process:
Download osm data using ox.graph_from_point
Save to Geopackage edges and nodes using ox.io.save_graph_geopackage, to allow user to add edges, extra roads in QGIS by digitising roads (with snapping) and save edits.
Convert edited edges back to OSMNX as a graph using ox.graph_from_gdfs.
At this point the 'ox.graph_from_gdfs' returns an empty Graph object. It appears to complain that the x attribute doesn't exist but the x and y attributes do exist in the geopackage nodes layer- so I don't understand the error.
Error:
coords = ((n, d["x"], d["y"]) for n, d in G.nodes(data=True))
KeyError: 'x'
Can anyone assist?
Code:
import osmnx as ox
import networkx as nx
import geopandas as gpd
from shapely.geometry import Point, LineString, MultiLineString,Polygon,MultiPolygon
print("OX ver: {}".format(ox.__version__))
print("NX ver: {}".format(nx.__version__))
geopPath = "osmnx_roaddata.gpkg"
xG = -1.08762688688598
yG = 53.9547041755247
orig = (yG,xG)
print(orig)
gdf_edges = gpd.read_file(geopPath, layer='edges')
gdf_nodes = gpd.read_file(geopPath, layer='nodes')
## Test to see if x exists in geodataframe- looks fine
#for index, row in gdf_nodes.iterrows():
# print("y: {}. x: {}".format(row['y'],row['x']))
print("######## Using Existing geopackage road edges and nodes")
### readthedocs: graph_attrs (dict) – the new G.graph attribute dict; if None, add crs as the only graph-level attribute
## don't know what the graph attribute dict should contain...or if providing the crs object is what is expected...
G = ox.graph_from_gdfs(gdf_nodes,gdf_edges) #, graph_attrs=gdf_nodes.crs)
print("G appears empty....: '{}'".format(G))
origin_node = ox.get_nearest_node(G, orig)
print("Roads geopackage now being used as variable 'G' graph object")
I understand I will probably need to calculate any missing nodes for new roads that have been digitised. But I should still be able to create a valid G graph object using 'ox.graph_from_gdfs' I thought before I encounter that issue. I've tested another geopackage with no additional roads or nodes other than the osmnx downloaded ones and same result.
Using OSMnx 0.16.0, NetworkX 2.5.
geopPath Geopackage Download
I will demonstrate how to do this with OSMnx v1.0 because it will be released in two days and provides slightly more robust support for converting GeoPandas GeoDataFrames to a NetworkX MultiDiGraph. See the docs for usage details.
Your problem appears to be that the u, v, and key columns in your edges GeoDataFrame contain null values, presumably from when you created them in QGIS. These are the unique identifiers of an edge and should be non-null integers. Both GeoDataFrames indexes should be unique.
import geopandas as gpd
import osmnx as ox
# create a graph, save as a GeoPackage
fp = 'graph.gpkg'
G = ox.graph_from_point((53.956748, -1.081676))
ox.save_graph_geopackage(G, fp)
# do some stuff in QGIS
# ensure the index attributes are non-null when you're finished
pass
# load GeoPackage as node/edge GeoDataFrames indexed as described in OSMnx docs
gdf_nodes = gpd.read_file(fp, layer='nodes').set_index('osmid')
gdf_edges = gpd.read_file(fp, layer='edges').set_index(['u', 'v', 'key'])
assert gdf_nodes.index.is_unique and gdf_edges.index.is_unique
# convert the node/edge GeoDataFrames to a MultiDiGraph
graph_attrs = {'crs': 'epsg:4326', 'simplified': True}
G2 = ox.graph_from_gdfs(gdf_nodes, gdf_edges, graph_attrs)

How do I extract each road in terms of the pixel coordinates from Google Map Screenshot and place them into different lists?

I'm working on a project related to road recognition from a standard Google Map view. Some navigation features will be added to the project later on.
I already extracted all the white pixels (representing road on the map) according to the RGB criteria. Also, I stored all the white pixel (roads) coordinates (2D) in one list named "all_roads". Now I want to extract each road in terms of the pixel coordinates and place them into different lists (one road in one list), but I'm lacking ideas.
I'd like to use Dijkstra's algorithm to calculate the shortest path between two points, but I need to create "nodes" on each road intersection. That's why I'd like to store each road in the corresponding list for further processing.
I hope someone could provide some ideas and methods. Thank you!
Note: The RGB criteria ("if" statements in "threshold" method) seems unnecessary for the chosen map screenshot, but it becomes useful in some other map screenshot with other road colours other than white. (NOT the point of the question anyway but I hope to avoid unnecessary confusion)
# Import numpy to enable numpy array
import numpy as np
# Import time to handle time-related task
import time
# Import mean to calculate the averages of the pixals
from statistics import mean
# Import cv2 to display the image
import cv2 as cv2
def threshold(imageArray):
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Purpose: Display a given image with road in white according to pixel RGBs
Argument(s): A matrix generated from a given image.
Return: A matrix of the same size but only displays white and black.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
newAr = imageArray
for eachRow in newAr:
for eachPix in eachRow:
if eachPix[0] == 253 and eachPix[1] == 242:
eachPix[0] = 255
eachPix[1] = 255
eachPix[2] = 255
else:
pass
return newAr
# Import the image
g1 = cv2.imread("1.png")
# fix the output image with resolution of 800 * 600
g1 = cv2.resize(g1,(800,600))
# Apply threshold method to the imported image
g2 = threshold(g1)
index = np.where(g2 == [(255,255,255)])
# x coordinate of the white pixels (roads)
print(index[1])
# y coordinate of the white pixels (roads)
print(index[0])
# Storing the 2D coordinates of white pixels (roads) in a list
all_roads = []
for i in range(len(index[0]))[0::3]:
all_roads.append([index[1][i], index[0][i]])
#Display the modified image
cv2.imshow('g2', g2)
cv2.waitKey(0)
cv2.destroyAllWindows()

How to change the limits for geo_shape in altair (python vega-lite)

I am trying to plot locations in three states in the US in python with Altair. I saw the tutorial about the us map but I am wondering if there is anyway to zoom the image to the only three states of interest, i.e. NY,NJ and CT.
Currently, I have the following code:
from vega_datasets import data
states = alt.topo_feature(data.us_10m.url, 'states')
# US states background
background = alt.Chart(states).mark_geoshape(
fill='lightgray',
stroke='white',
limit=1000
).properties(
title='US State Capitols',
width=700,
height=400
).project("albers")
points=alt.Chart(accts).mark_point().encode(
longitude = "longitude",
latitude = "latitude",
color = "Group")
background+points
I inspected the us_10m.url data set and seems like there is no field which specifies the individual states. So I am hoping if I could just somehow change the xlim and ylim for the background to [-80,-70] and [35,45] for example. I want to zoom in to the regions where there are data points(blue dots).
Could someone kindly show me how to do that? Thanks!!
Update
There is a field called ID in the JSON file and I manually found out that NJ is 34, NY is 36 and CT is 9. Is there a way to filter on these IDs? That will get the job done!
Alright seems like the selection/zoom/xlim/ylim feature for geotype is not supported yet:
Document and add warning that geo-position doesn't support selection yet #3305
So I end up with a hackish way to solve this problem by first filtering based on the IDs using pure python. Basically, load the JSON file into a dictionary and then change the value field before converting the dictionary to topojson format. Below is an example for 5 states,PA,NJ,NY,CT,RI and MA.
import altair as alt
from vega_datasets import data
# Load the data, which is loaded as a dict object
us_10m = data.us_10m()
# Select the geometries under states under objects, filter on id (9,25,34,36,42,44)
us_10m['objects']['states']['geometries']=[item for item in us_10m['objects'] \
['states']['geometries'] if item['id'] in [9,25,34,36,42,44]]
# Make the topojson data
states = alt.Data(
values=us_10m,
format=alt.TopoDataFormat(feature='states',type='topojson'))
# Plot background (now only has 5 states)
background = alt.Chart(states).mark_geoshape(
fill='lightgray',
stroke='white',
limit=1000
).properties(
title='US State Capitols',
width=700,
height=400
).project("mercator")
# Plot the points
points=alt.Chart(accts).mark_circle(size=60).encode(
longitude = "longitude",
latitude = "latitude",
color = "Group").project("mercator")
# Overlay the two plots
background+points
The resulting plot looks ok:

Reprojecting Xarray Dataset

I'm trying to reproject a Lambert Conformal dataset to Plate Carree. I know that this can easily be done visually using cartopy. However, I'm trying to create a new dataset rather than just show a reprojected image. Below is methodology I have mapped out but I'm unable to subset the dataset properly (Python 3.5, MacOSx).
from siphon.catalog import TDSCatalog
import xarray as xr
from xarray.backends import NetCDF4DataStore
import numpy as np
import cartopy.crs as ccrs
from scipy.interpolate import griddata
import numpy.ma as ma
from pyproj import Proj, transform
import metpy
# Declare bounding box
min_lon = -78
min_lat = 36
max_lat = 40
max_lon = -72
boundinglat = [min_lat, max_lat]
boundinglon = [min_lon, max_lon]
# Load the dataset
cat = TDSCatalog('https://thredds.ucar.edu/thredds/catalog/grib/NCEP/HRRR/CONUS_2p5km/latest.xml')
dataset_name = sorted(cat.datasets.keys())[-1]
dataset = cat.datasets[dataset_name]
ds = dataset.remote_access(service='OPENDAP')
ds = NetCDF4DataStore(ds)
ds = xr.open_dataset(ds)
# parse the temperature at 850 and # 0z reftime
tempiso = ds.metpy.parse_cf('Temperature_isobaric')
t850 = tempiso[0][2]
# transform bounding lat/lons to src_proj
src_proj = tempiso.metpy.cartopy_crs #aka lambert conformal conical
extents = src_proj.transform_points(ccrs.PlateCarree(), np.array(boundinglon), np.array(boundinglat))
# subset the data using the indexes of the closest values to the src_proj extents
t850_subset = t850[(np.abs(tempiso.y.values - extents[1][0])).argmin():(np.abs(tempiso.y.values - extents[1][1])).argmin()][(np.abs(tempiso.x.values - extents[0][1])).argmin():(np.abs(tempiso.x.values - extents[0][0])).argmin()]
# t850_subset should be a small, reshaped dataset, but it's shape is 0x2145
# now use nplinspace, npmeshgrid & scipy interpolate to reproject
My transform point > find nearest value subsetting isn't working. It's claiming the closest points are outside the realm of the dataset. As noted, I plan to use nplinspace, npmeshgrid and scipy interpolate to create a new, square lat/lon dataset from t850_subset.
Is there an easier way to resize & reproject an xarray dataset?
Your easiest path forward is to take advantage of xarray's ability to do pandas-like data selection; this is IMO the best part of xarray. Replace your last two lines with:
# By transposing the result of transform_points, we can unpack the
# x and y coordinates into individual arrays.
x_lim, y_lim, _ = src_proj.transform_points(ccrs.PlateCarree(),
np.array(boundinglon), np.array(boundinglat)).T
t850_subset = t850.sel(x=slice(*x_lim), y=slice(*y_lim))
You can find more information in the documentation on xarray's selection and indexing functionality. You would probably also be interested in xarray's built-in support for interpolation. And if interpolation methods beyond SciPy's are of interest, MetPy also has a suite of other interpolation methods.
We have various "regridding" methods in Iris, if that isn't too much of a context switch for you.
Xarray explains its relationship to Iris here, and provides a to_iris() method.

Resources