187 lines
9.9 KiB
Python
187 lines
9.9 KiB
Python
#------------------------------------------------------------------------------
|
|
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
|
#------------------------------------------------------------------------------
|
|
|
|
#------------------------------------------------------------------------------
|
|
# spatial_to_geopandas.py
|
|
# GeoPandas is a popular python library for working with geospatial data.
|
|
# GeoPandas extends the Pandas data analysis library with geospatial support
|
|
# using the Shapely library for geometry object support.
|
|
#
|
|
# See http://geopandas.org, https://pandas.pydata.org,
|
|
# and https://github.com/Toblerity/Shapely.
|
|
#
|
|
# This example shows how to bring geometries from Oracle Spatial (SDO_GEOMETRY
|
|
# data type) into GeoPandas and perform a simple spatial operation. While the
|
|
# spatial operation we perform in Python could have been performed in the
|
|
# Oracle database, this example targets use cases where Python with GeoPandas
|
|
# is being used to combine and work with geospatial data from numerous
|
|
# additional sources such as files and web services.
|
|
#
|
|
# This script requires cx_Oracle (5.3 and higher) as well as GeoPandas and its
|
|
# dependencies (see http://geopandas.org/install.html).
|
|
#------------------------------------------------------------------------------
|
|
|
|
from shapely.wkb import loads
|
|
import geopandas as gpd
|
|
|
|
import cx_Oracle as oracledb
|
|
import sample_env
|
|
|
|
# create Oracle connection and cursor objects
|
|
connection = oracledb.connect(sample_env.get_main_connect_string())
|
|
cursor = connection.cursor()
|
|
|
|
# enable autocommit to avoid the additional round trip to the database to
|
|
# perform a commit; this should not be used if multiple statements must be
|
|
# executed for a single transaction
|
|
connection.autocommit = True
|
|
|
|
# define output type handler to fetch LOBs, avoiding the second round trip to
|
|
# the database to read the LOB contents
|
|
def output_type_handler(cursor, name, default_type, size, precision, scale):
|
|
if default_type == oracledb.BLOB:
|
|
return cursor.var(oracledb.LONG_BINARY, arraysize=cursor.arraysize)
|
|
connection.outputtypehandler = output_type_handler
|
|
|
|
# drop and create table
|
|
print("Dropping and creating table...")
|
|
cursor.execute("""
|
|
begin
|
|
execute immediate 'drop table TestStates';
|
|
exception when others then
|
|
if sqlcode <> -942 then
|
|
raise;
|
|
end if;
|
|
end;""")
|
|
cursor.execute("""
|
|
create table TestStates (
|
|
state VARCHAR2(30) not null,
|
|
geometry SDO_GEOMETRY not null
|
|
)""")
|
|
|
|
# acquire types used for creating SDO_GEOMETRY objects
|
|
type_obj = connection.gettype("MDSYS.SDO_GEOMETRY")
|
|
element_info_type_obj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY")
|
|
ordinate_type_obj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY")
|
|
|
|
# define function for creating an SDO_GEOMETRY object
|
|
def create_geometry_obj(*ordinates):
|
|
geometry = type_obj.newobject()
|
|
geometry.SDO_GTYPE = 2003
|
|
geometry.SDO_SRID = 8307
|
|
geometry.SDO_ELEM_INFO = element_info_type_obj.newobject()
|
|
geometry.SDO_ELEM_INFO.extend([1, 1003, 1])
|
|
geometry.SDO_ORDINATES = ordinate_type_obj.newobject()
|
|
geometry.SDO_ORDINATES.extend(ordinates)
|
|
return geometry
|
|
|
|
# create SDO_GEOMETRY objects for three adjacent states in the USA
|
|
geometry_nevada = create_geometry_obj(-114.052025, 37.103989, -114.049797,
|
|
37.000423, -113.484375, 37, -112.898598, 37.000401,-112.539604,
|
|
37.000683, -112, 37.000977, -111.412048, 37.001514, -111.133018,
|
|
37.00079,-110.75, 37.003201, -110.5, 37.004265, -110.469505, 36.998001,
|
|
-110, 36.997967, -109.044571,36.999088, -109.045143, 37.375,
|
|
-109.042824, 37.484692, -109.040848, 37.881176, -109.041405,38.153027,
|
|
-109.041107, 38.1647, -109.059402, 38.275501, -109.059296, 38.5,
|
|
-109.058868, 38.719906,-109.051765, 39, -109.050095, 39.366699,
|
|
-109.050697, 39.4977, -109.050499, 39.6605, -109.050156,40.222694,
|
|
-109.047577, 40.653641, -109.0494, 41.000702, -109.2313, 41.002102,
|
|
-109.534233,40.998184, -110, 40.997398, -110.047768, 40.997696, -110.5,
|
|
40.994801, -111.045982, 40.998013,-111.045815, 41.251774, -111.045097,
|
|
41.579899, -111.045944, 42.001633, -111.506493, 41.999588,-112.108742,
|
|
41.997677, -112.16317, 41.996784, -112.172562, 41.996643, -112.192184,
|
|
42.001244,-113, 41.998314, -113.875, 41.988091, -114.040871, 41.993805,
|
|
-114.038803, 41.884899, -114.041306,41, -114.04586, 40.116997,
|
|
-114.046295, 39.906101, -114.046898, 39.542801, -114.049026, 38.67741,
|
|
-114.049339, 38.572968, -114.049095, 38.14864, -114.0476,
|
|
37.80946,-114.05098, 37.746284, -114.051666, 37.604805, -114.052025,
|
|
37.103989)
|
|
geometry_wyoming = create_geometry_obj(-111.045815, 41.251774, -111.045982,
|
|
40.998013, -110.5, 40.994801, -110.047768, 40.997696, -110, 40.997398,
|
|
-109.534233, 40.998184, -109.2313, 41.002102, -109.0494, 41.000702,
|
|
-108.525368, 40.999634, -107.917793, 41.002071, -107.317177, 41.002956,
|
|
-106.857178, 41.002697, -106.455704, 41.002167, -106.320587, 40.999153,
|
|
-106.189987, 40.997604, -105.729874, 40.996906, -105.276604, 40.998188,
|
|
-104.942848, 40.998226, -104.625, 41.00145, -104.052742, 41.001423,
|
|
-104.051781, 41.39333, -104.052032, 41.564301, -104.052185, 41.697983,
|
|
-104.052109, 42.001736, -104.052277, 42.611626, -104.052643, 43.000614,
|
|
-104.054337, 43.47784, -104.054298, 43.503101, -104.055, 43.8535,
|
|
-104.054108, 44.141102, -104.054001, 44.180401, -104.055458, 44.570877,
|
|
-104.057205, 44.997444, -104.664658, 44.998631, -105.037872, 45.000359,
|
|
-105.088867, 45.000462, -105.912819, 45.000957, -105.927612, 44.99366,
|
|
-106.024239, 44.993591, -106.263, 44.993801, -107.054871, 44.996384,
|
|
-107.133545, 45.000141, -107.911095, 45.001343, -108.248672, 44.999504,
|
|
-108.620628, 45.000328, -109.082314, 44.999664, -109.102745, 45.005955,
|
|
-109.797951, 45.002247, -110.000771, 45.003502, -110.10936, 45.003967,
|
|
-110.198761, 44.99625, -110.286026, 44.99691, -110.361946, 45.000656,
|
|
-110.402176, 44.993874, -110.5, 44.992355, -110.704506, 44.99239,
|
|
-110.784241, 45.003021, -111.05442, 45.001392, -111.054558, 44.666336,
|
|
-111.048203, 44.474144, -111.046272, 43.983456, -111.044724, 43.501213,
|
|
-111.043846, 43.3158, -111.043381, 43.02013, -111.042786, 42.719578,
|
|
-111.045967, 42.513187, -111.045944, 42.001633, -111.045097, 41.579899,
|
|
-111.045815, 41.251774)
|
|
geometry_colorado = create_geometry_obj(-109.045143, 37.375, -109.044571,
|
|
36.999088, -108.378571, 36.999516, -107.481133, 37, -107.420311, 37,
|
|
-106.876701, 37.00013, -106.869209, 36.992416, -106.475639, 36.993748,
|
|
-106.006058, 36.995327, -105.717834, 36.995823, -105.220055, 36.995144,
|
|
-105.154488, 36.995239, -105.028671, 36.992702, -104.407616, 36.993446,
|
|
-104.007324, 36.996216, -103.085617, 37.000244, -103.001709, 37.000084,
|
|
-102.986488, 36.998505, -102.759384, 37, -102.69767, 36.995132,
|
|
-102.041794, 36.993061, -102.041191, 37.389172, -102.04113, 37.644268,
|
|
-102.041695, 37.738529, -102.043938, 38.262466, -102.044113, 38.268803,
|
|
-102.04483, 38.615234, -102.044762, 38.697556, -102.046112, 39.047035,
|
|
-102.046707, 39.133144, -102.049301, 39.568176, -102.049347, 39.574062,
|
|
-102.051277, 40.00309, -102.051117, 40.34922, -102.051003, 40.440018,
|
|
-102.050873, 40.697556, -102.050835, 40.749596, -102.051155, 41.002384,
|
|
-102.620567, 41.002609, -102.652992, 41.002342, -103.382011, 41.00227,
|
|
-103.574036, 41.001736, -104.052742, 41.001423, -104.625, 41.00145,
|
|
-104.942848, 40.998226, -105.276604, 40.998188, -105.729874, 40.996906,
|
|
-106.189987, 40.997604, -106.320587, 40.999153, -106.455704, 41.002167,
|
|
-106.857178, 41.002697, -107.317177, 41.002956, -107.917793, 41.002071,
|
|
-108.525368, 40.999634, -109.0494, 41.000702, -109.047577, 40.653641,
|
|
-109.050156, 40.222694, -109.050499, 39.6605, -109.050697, 39.4977,
|
|
-109.050095, 39.366699, -109.051765, 39, -109.058868, 38.719906,
|
|
-109.059296, 38.5, -109.059402, 38.275501, -109.041107, 38.1647,
|
|
-109.041405, 38.153027, -109.040848, 37.881176, -109.042824, 37.484692,
|
|
-109.045143, 37.375)
|
|
|
|
# Insert rows for test states. If we were analyzing these geometries in Oracle
|
|
# we would also add Spatial metadata and indexes. However in this example we
|
|
# are only storing the geometries so that we load them back into Python, so we
|
|
# will skip the metadata and indexes.
|
|
print("Adding rows to table...")
|
|
data = [
|
|
('Nevada', geometry_nevada),
|
|
('Colorado', geometry_colorado),
|
|
('Wyoming', geometry_wyoming)
|
|
]
|
|
cursor.executemany('insert into TestStates values (:state, :obj)', data)
|
|
|
|
# We now have test geometries in Oracle Spatial (SDO_GEOMETRY) and will next
|
|
# bring them back into Python to analyze with GeoPandas. GeoPandas is able to
|
|
# consume geometries in the Well Known Text (WKT) and Well Known Binary (WKB)
|
|
# formats. Oracle database includes utility functions to return SDO_GEOMETRY as
|
|
# both WKT and WKB. Therefore we use that utility function in the query below
|
|
# to provide results in a format readily consumable by GeoPandas. These utility
|
|
# functions were introduced in Oracle 10g. We use WKB here; however the same
|
|
# process applies for WKT.
|
|
cursor.execute("""
|
|
SELECT state, sdo_util.to_wkbgeometry(geometry)
|
|
FROM TestStates""")
|
|
gdf = gpd.GeoDataFrame(cursor.fetchall(), columns=['state', 'wkbgeometry'])
|
|
|
|
# create GeoSeries to replace the WKB geometry column
|
|
gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x)))
|
|
del gdf['wkbgeometry']
|
|
|
|
# display the GeoDataFrame
|
|
print()
|
|
print(gdf)
|
|
|
|
# perform a basic GeoPandas operation (unary_union)
|
|
# to combine the 3 adjacent states into 1 geometry
|
|
print()
|
|
print("GeoPandas combining the 3 geometries into a single geometry...")
|
|
print(gdf.unary_union)
|