#------------------------------------------------------------------------------ # Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # SpatialToGeoPandas.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 __future__ import print_function import SampleEnv import cx_Oracle from shapely.wkb import loads import geopandas as gpd # create Oracle connection and cursor objects connection = cx_Oracle.connect(SampleEnv.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 OutputTypeHandler(cursor, name, defaultType, size, precision, scale): if defaultType == cx_Oracle.BLOB: return cursor.var(cx_Oracle.LONG_BINARY, arraysize = cursor.arraysize) connection.outputtypehandler = OutputTypeHandler # 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 typeObj = connection.gettype("MDSYS.SDO_GEOMETRY") elementInfoTypeObj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") ordinateTypeObj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY") # define function for creating an SDO_GEOMETRY object def CreateGeometryObj(*ordinates): geometry = typeObj.newobject() geometry.SDO_GTYPE = 2003 geometry.SDO_SRID = 8307 geometry.SDO_ELEM_INFO = elementInfoTypeObj.newobject() geometry.SDO_ELEM_INFO.extend([1, 1003, 1]) geometry.SDO_ORDINATES = ordinateTypeObj.newobject() geometry.SDO_ORDINATES.extend(ordinates) return geometry # create SDO_GEOMETRY objects for three adjacent states in the USA geometryNevada = CreateGeometryObj(-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) geometryWyoming = CreateGeometryObj(-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) geometryColorado = CreateGeometryObj(-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', geometryNevada), ('Colorado', geometryColorado), ('Wyoming', geometryWyoming) ] 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)