|
| 1 | +#------------------------------------------------------------------------------ |
| 2 | +# Copyright 2018, Oracle and/or its affiliates. All rights reserved. |
| 3 | +#------------------------------------------------------------------------------ |
| 4 | + |
| 5 | +#------------------------------------------------------------------------------ |
| 6 | +# SpatialToGeoPandas.py |
| 7 | +# GeoPandas is a popular python library for working with geospatial data. |
| 8 | +# GeoPandas extends the Pandas data analysis library with geospatial support |
| 9 | +# using the Shapely library for geometry object support. |
| 10 | +# |
| 11 | +# See http://geopandas.org, https://pandas.pydata.org, |
| 12 | +# and https://github.com/Toblerity/Shapely. |
| 13 | +# |
| 14 | +# This example shows how to bring geometries from Oracle Spatial (SDO_GEOMETRY |
| 15 | +# data type) into GeoPandas and perform a simple spatial operation. While the |
| 16 | +# spatial operation we perform in Python could have been performed in the |
| 17 | +# Oracle database, this example targets use cases where Python with GeoPandas |
| 18 | +# is being used to combine and work with geospatial data from numerous |
| 19 | +# additional sources such as files and web services. |
| 20 | +# |
| 21 | +# This script requires cx_Oracle 5.3 and higher. |
| 22 | +#------------------------------------------------------------------------------ |
| 23 | + |
| 24 | +from __future__ import print_function |
| 25 | + |
| 26 | +import SampleEnv |
| 27 | +import cx_Oracle |
| 28 | +from shapely.wkb import loads |
| 29 | +import geopandas as gpd |
| 30 | + |
| 31 | +# create Oracle connection and cursor objects |
| 32 | +connection = cx_Oracle.Connection(SampleEnv.MAIN_CONNECT_STRING) |
| 33 | +cursor = connection.cursor() |
| 34 | + |
| 35 | +# enable autocommit to avoid the additional round trip to the database to |
| 36 | +# perform a commit; this should not be used if multiple statements must be |
| 37 | +# executed for a single transaction |
| 38 | +connection.autocommit = True |
| 39 | + |
| 40 | +# define output type handler to fetch LOBs, avoiding the second round trip to |
| 41 | +# the database to read the LOB contents |
| 42 | +def OutputTypeHandler(cursor, name, defaultType, size, precision, scale): |
| 43 | + if defaultType == cx_Oracle.BLOB: |
| 44 | + return cursor.var(cx_Oracle.LONG_BINARY, arraysize = cursor.arraysize) |
| 45 | +connection.outputtypehandler = OutputTypeHandler |
| 46 | + |
| 47 | +# drop and create table |
| 48 | +print("Dropping and creating table...") |
| 49 | +cursor.execute(""" |
| 50 | + begin |
| 51 | + execute immediate 'drop table TestStates'; |
| 52 | + exception when others then |
| 53 | + if sqlcode <> -942 then |
| 54 | + raise; |
| 55 | + end if; |
| 56 | + end;""") |
| 57 | +cursor.execute(""" |
| 58 | + create table TestStates ( |
| 59 | + state VARCHAR2(30) not null, |
| 60 | + geometry SDO_GEOMETRY not null |
| 61 | + )""") |
| 62 | + |
| 63 | +# acquire types used for creating SDO_GEOMETRY objects |
| 64 | +typeObj = connection.gettype("MDSYS.SDO_GEOMETRY") |
| 65 | +elementInfoTypeObj = connection.gettype("MDSYS.SDO_ELEM_INFO_ARRAY") |
| 66 | +ordinateTypeObj = connection.gettype("MDSYS.SDO_ORDINATE_ARRAY") |
| 67 | + |
| 68 | +# define function for creating an SDO_GEOMETRY object |
| 69 | +def CreateGeometryObj(*ordinates): |
| 70 | + geometry = typeObj.newobject() |
| 71 | + geometry.SDO_GTYPE = 2003 |
| 72 | + geometry.SDO_SRID = 8307 |
| 73 | + geometry.SDO_ELEM_INFO = elementInfoTypeObj.newobject() |
| 74 | + geometry.SDO_ELEM_INFO.extend([1, 1003, 1]) |
| 75 | + geometry.SDO_ORDINATES = ordinateTypeObj.newobject() |
| 76 | + geometry.SDO_ORDINATES.extend(ordinates) |
| 77 | + return geometry |
| 78 | + |
| 79 | +# create SDO_GEOMETRY objects for three adjacent states in the USA |
| 80 | +geometryNevada = CreateGeometryObj(-114.052025, 37.103989, -114.049797, |
| 81 | + 37.000423, -113.484375, 37, -112.898598, 37.000401,-112.539604, |
| 82 | + 37.000683, -112, 37.000977, -111.412048, 37.001514, -111.133018, |
| 83 | + 37.00079,-110.75, 37.003201, -110.5, 37.004265, -110.469505, 36.998001, |
| 84 | + -110, 36.997967, -109.044571,36.999088, -109.045143, 37.375, |
| 85 | + -109.042824, 37.484692, -109.040848, 37.881176, -109.041405,38.153027, |
| 86 | + -109.041107, 38.1647, -109.059402, 38.275501, -109.059296, 38.5, |
| 87 | + -109.058868, 38.719906,-109.051765, 39, -109.050095, 39.366699, |
| 88 | + -109.050697, 39.4977, -109.050499, 39.6605, -109.050156,40.222694, |
| 89 | + -109.047577, 40.653641, -109.0494, 41.000702, -109.2313, 41.002102, |
| 90 | + -109.534233,40.998184, -110, 40.997398, -110.047768, 40.997696, -110.5, |
| 91 | + 40.994801, -111.045982, 40.998013,-111.045815, 41.251774, -111.045097, |
| 92 | + 41.579899, -111.045944, 42.001633, -111.506493, 41.999588,-112.108742, |
| 93 | + 41.997677, -112.16317, 41.996784, -112.172562, 41.996643, -112.192184, |
| 94 | + 42.001244,-113, 41.998314, -113.875, 41.988091, -114.040871, 41.993805, |
| 95 | + -114.038803, 41.884899, -114.041306,41, -114.04586, 40.116997, |
| 96 | + -114.046295, 39.906101, -114.046898, 39.542801, -114.049026, 38.67741, |
| 97 | + -114.049339, 38.572968, -114.049095, 38.14864, -114.0476, |
| 98 | + 37.80946,-114.05098, 37.746284, -114.051666, 37.604805, -114.052025, |
| 99 | + 37.103989) |
| 100 | +geometryWyoming = CreateGeometryObj(-111.045815, 41.251774, -111.045982, |
| 101 | + 40.998013, -110.5, 40.994801, -110.047768, 40.997696, -110, 40.997398, |
| 102 | + -109.534233, 40.998184, -109.2313, 41.002102, -109.0494, 41.000702, |
| 103 | + -108.525368, 40.999634, -107.917793, 41.002071, -107.317177, 41.002956, |
| 104 | + -106.857178, 41.002697, -106.455704, 41.002167, -106.320587, 40.999153, |
| 105 | + -106.189987, 40.997604, -105.729874, 40.996906, -105.276604, 40.998188, |
| 106 | + -104.942848, 40.998226, -104.625, 41.00145, -104.052742, 41.001423, |
| 107 | + -104.051781, 41.39333, -104.052032, 41.564301, -104.052185, 41.697983, |
| 108 | + -104.052109, 42.001736, -104.052277, 42.611626, -104.052643, 43.000614, |
| 109 | + -104.054337, 43.47784, -104.054298, 43.503101, -104.055, 43.8535, |
| 110 | + -104.054108, 44.141102, -104.054001, 44.180401, -104.055458, 44.570877, |
| 111 | + -104.057205, 44.997444, -104.664658, 44.998631, -105.037872, 45.000359, |
| 112 | + -105.088867, 45.000462, -105.912819, 45.000957, -105.927612, 44.99366, |
| 113 | + -106.024239, 44.993591, -106.263, 44.993801, -107.054871, 44.996384, |
| 114 | + -107.133545, 45.000141, -107.911095, 45.001343, -108.248672, 44.999504, |
| 115 | + -108.620628, 45.000328, -109.082314, 44.999664, -109.102745, 45.005955, |
| 116 | + -109.797951, 45.002247, -110.000771, 45.003502, -110.10936, 45.003967, |
| 117 | + -110.198761, 44.99625, -110.286026, 44.99691, -110.361946, 45.000656, |
| 118 | + -110.402176, 44.993874, -110.5, 44.992355, -110.704506, 44.99239, |
| 119 | + -110.784241, 45.003021, -111.05442, 45.001392, -111.054558, 44.666336, |
| 120 | + -111.048203, 44.474144, -111.046272, 43.983456, -111.044724, 43.501213, |
| 121 | + -111.043846, 43.3158, -111.043381, 43.02013, -111.042786, 42.719578, |
| 122 | + -111.045967, 42.513187, -111.045944, 42.001633, -111.045097, 41.579899, |
| 123 | + -111.045815, 41.251774) |
| 124 | +geometryColorado = CreateGeometryObj(-109.045143, 37.375, -109.044571, |
| 125 | + 36.999088, -108.378571, 36.999516, -107.481133, 37, -107.420311, 37, |
| 126 | + -106.876701, 37.00013, -106.869209, 36.992416, -106.475639, 36.993748, |
| 127 | + -106.006058, 36.995327, -105.717834, 36.995823, -105.220055, 36.995144, |
| 128 | + -105.154488, 36.995239, -105.028671, 36.992702, -104.407616, 36.993446, |
| 129 | + -104.007324, 36.996216, -103.085617, 37.000244, -103.001709, 37.000084, |
| 130 | + -102.986488, 36.998505, -102.759384, 37, -102.69767, 36.995132, |
| 131 | + -102.041794, 36.993061, -102.041191, 37.389172, -102.04113, 37.644268, |
| 132 | + -102.041695, 37.738529, -102.043938, 38.262466, -102.044113, 38.268803, |
| 133 | + -102.04483, 38.615234, -102.044762, 38.697556, -102.046112, 39.047035, |
| 134 | + -102.046707, 39.133144, -102.049301, 39.568176, -102.049347, 39.574062, |
| 135 | + -102.051277, 40.00309, -102.051117, 40.34922, -102.051003, 40.440018, |
| 136 | + -102.050873, 40.697556, -102.050835, 40.749596, -102.051155, 41.002384, |
| 137 | + -102.620567, 41.002609, -102.652992, 41.002342, -103.382011, 41.00227, |
| 138 | + -103.574036, 41.001736, -104.052742, 41.001423, -104.625, 41.00145, |
| 139 | + -104.942848, 40.998226, -105.276604, 40.998188, -105.729874, 40.996906, |
| 140 | + -106.189987, 40.997604, -106.320587, 40.999153, -106.455704, 41.002167, |
| 141 | + -106.857178, 41.002697, -107.317177, 41.002956, -107.917793, 41.002071, |
| 142 | + -108.525368, 40.999634, -109.0494, 41.000702, -109.047577, 40.653641, |
| 143 | + -109.050156, 40.222694, -109.050499, 39.6605, -109.050697, 39.4977, |
| 144 | + -109.050095, 39.366699, -109.051765, 39, -109.058868, 38.719906, |
| 145 | + -109.059296, 38.5, -109.059402, 38.275501, -109.041107, 38.1647, |
| 146 | + -109.041405, 38.153027, -109.040848, 37.881176, -109.042824, 37.484692, |
| 147 | + -109.045143, 37.375) |
| 148 | + |
| 149 | +# Insert rows for test states. If we were analyzing these geometries in Oracle |
| 150 | +# we would also add Spatial metadata and indexes. However in this example we |
| 151 | +# are only storing the geometries so that we load them back into Python, so we |
| 152 | +# will skip the metadata and indexes. |
| 153 | +print("Adding rows to table...") |
| 154 | +data = [ |
| 155 | + ('Nevada', geometryNevada), |
| 156 | + ('Colorado', geometryColorado), |
| 157 | + ('Wyoming', geometryWyoming) |
| 158 | +] |
| 159 | +cursor.executemany('insert into TestStates values (:state, :obj)', data) |
| 160 | + |
| 161 | +# We now have test geometries in Oracle Spatial (SDO_GEOMETRY) and will next |
| 162 | +# bring them back into Python to analyze with GeoPandas. GeoPandas is able to |
| 163 | +# consume geometries in the Well Known Text (WKT) and Well Known Binary (WKB) |
| 164 | +# formats. Oracle database includes utility functions to return SDO_GEOMETRY as |
| 165 | +# both WKT and WKB. Therefore we use that utility function in the query below |
| 166 | +# to provide results in a format readily consumable by GeoPandas. These utility |
| 167 | +# functions were introduced in Oracle 10g. We use WKB here; however the same |
| 168 | +# process applies for WKT. |
| 169 | +cursor.execute(""" |
| 170 | + SELECT state, sdo_util.to_wkbgeometry(geometry) |
| 171 | + FROM TestStates""") |
| 172 | +gdf = gpd.GeoDataFrame(cursor.fetchall(), columns = ['state', 'wkbgeometry']) |
| 173 | + |
| 174 | +# create GeoSeries to replace the WKB geometry column |
| 175 | +gdf['geometry'] = gpd.GeoSeries(gdf['wkbgeometry'].apply(lambda x: loads(x))) |
| 176 | +del gdf['wkbgeometry'] |
| 177 | + |
| 178 | +# display the GeoDataFrame |
| 179 | +print() |
| 180 | +print(gdf) |
| 181 | + |
| 182 | +# perform a basic GeoPandas operation (unary_union) |
| 183 | +# to combine the 3 adjacent states into 1 geometry |
| 184 | +print() |
| 185 | +print("GeoPandas combining the 3 geometries into a single geometry...") |
| 186 | +print(gdf.unary_union) |
| 187 | + |
0 commit comments