Package: kamutils
Library for converting Oracle SDO GEOMETRY into spatial objects which implements the__geo_interface__protocol (GeoJSON like dict).Supported geometry types are : Point, Linestring, Polygon, MultiPoint, MultiLinestring, MultiPolygon and Collection.Converted geometry can be used directly by other packages that support__geo_interface__likeShapelyorGeoPandas.Kamutilsprovides a WKT representation (Well Known Text) of the converted geometry.Kamutilsallows geometry conversion for database versions that does not implementSDO_UTILmethods likeTO_WKTGEOMETRYorTO_JSON/TO_GEOJSON, or implement them partially.For example, you may encounter an error3D geometries are not supported by geometry WKB/WKT generationwith methodSDO_UTIL.TO_WKTGEOMETRYif you have 3D geometry with X,Y,Z coordinates.Kamutilsincludes other miscellaneous utilities.Table of ContentsLicenseRequirementsOracle SDO GEOMETRY conversionExample 1 : read and convert geometry withconvert_from_sdo_geometry()Example 2 : interoperabilityExample 3 : conversion to ArcGis geometryClasses and fonctionsGeometry typesError codesLimitationsOther utilitiesRangeSetLicenseKamutilsis licensed under CeCILL-B license, a BSD-like french/european license.License in English :https://www.cecill.info/licences/Licence_CeCILL-B_V1-en.htmlLicense in French :https://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.htmlRequirementsKamutilshas been tested with :Python 3.7oracledb 1.1.1Dependencies : noneOracle SDO GEOMETRY conversionExample 1 : read and convert geometry withconvert_from_sdo_geometry()import oracledb
from kamutils.spatial import convert_from_sdo_geometry

oracledb.init_oracle_client()
connection = oracledb.connect(user="Test", password="********", dsn="MY_DSN")

with connection.cursor() as cursor:
	cursor.execute("select geometry, ID from MY_BASE.MY_TABLE where ID='l1'")
	row = cursor.fetchone()
	if row:
		spatial_object = convert_from_sdo_geometry(row[0])
		if spatial_object.error is None:
			print('converted object :')
			print(spatial_object)
			print('srid :',spatial_object.srid)
			print('error :',spatial_object.error)
			print('to WKT:')
			print(spatial_object.wkt())
		else:
			print(spatial_object.error)
			print('id :',spatial_object.error.id)
			print('msg :',spatial_object.error.msg)output :converted object :
<SpatialObject - type:LineString srid:None {'type': 'LineString', 'coordinates': [(10.0, 25.0, 0.0), (20.0, 30.0, 0.0), (25.0, 25.0, 0.0), (30.0, 30.0, 0.0)], 'measures': None}>
srid : None
error : None
to WKT:
LINESTRING Z(10.0 25.0 0.0,20.0 30.0 0.0,25.0 25.0 0.0,30.0 30.0 0.0)output in case of error :Error 2 : Unknown geometry : SDO_GTYPE = 0
id : 2
msg : Unknown geometry : SDO_GTYPE = 0Example 2 : interoperability. . .
spo = convert_from_sdo_geometry(row[0])
import json
print('json dump :')
print(json.dumps(spo.__geo_interface__))
print()

from shapely import geometry
obj=geometry.shape(spo)
print('shapely :')
print(type(obj))
print(obj)
print()

import geojson
obj=geojson.mapping.to_mapping(spo)
print('geojson :')
print(type(obj))
print(obj)
print()

import plpygis
obj=plpygis.Geometry.shape(spo)
print('plpygis :')
print(type(obj))
print(obj)output :json dump :
{"type": "LineString", "coordinates": [[10.0, 25.0, 0.0], [20.0, 30.0, 0.0], [25.0, 25.0, 0.0], [30.0, 30.0, 0.0]]}

shapely :
<class 'shapely.geometry.linestring.LineString'>
LINESTRING Z (10 25 0, 20 30 0, 25 25 0, 30 30 0)

geojson :
<class 'dict'>
{'type': 'LineString', 'coordinates': [(10.0, 25.0, 0.0), (20.0, 30.0, 0.0), (25.0, 25.0, 0.0), (30.0, 30.0, 0.0)]}

plpygis :
<class 'plpygis.geometry.LineString'>
01020000800400000000000000000024400000000000003940000000000000000000000000000034400000000000003e4
000000000000000000000000000003940000000000000394000000000000000000000000000003e400000000000003e40
0000000000000000Example 3 : conversion to ArcGis geometryimport arcpy
. . .
spo = convert_from_sdo_geometry(row[0])
if spo.srid is None:
	# supposing you know the actual coordinates system of the source geometry
	srid = KNOWN_SRID
else:
	srid = spo.srid
arcpy_geom = arcpy.FromWKT(spo.wkt(), arcpy.SpatialReference(srid))Classes and fonctionsFunctionconvert_from_sdo_geometryModule :kamutils.spatialConverts aSDO_GEOMETRYobject and returns aSpatialObjectinstance.ClassSpatialObjectModule :kamutils.spatialProperties :type: returns the geometry type (seeGeometry types constants)srid: returns the srid (Spatial Reference IDentifier) wich identifies the coordinate system of the geometry. If not defined, returnNoneerror: returns the conversion error if any orNone.erroris an instance of classError__geo_interface__: returns a GeoJSON dict representation of the geometry. The geometry is not converted to EPSG:4326 regardless of its srid.is_valid: returnsTrueif the geometry is valid, i.e. there is no conversion error,Falsein other casesMethods :wkt(): returns the WKT representation of the geometrygeometries(): returns an iterator over all sub-objects in case of a collection (flattening all sub-collections). If theSpatialObjectis not a collection, the iterator returns the object itself.ClassErrorModule :kamutils.exceptionsAttributes :id: returns the error id (seeError codes)msg: returns the error messageGeometry typesTo import Geometry types constants :from kamutils.spatial import GEOMETRY_TYPEConstant list :Constant nameValueGEOMETRY_TYPE.POINT1GEOMETRY_TYPE.LINESTRING2GEOMETRY_TYPE.POLYGON3GEOMETRY_TYPE.COLLECTION4GEOMETRY_TYPE.MULTIPOINT5GEOMETRY_TYPE.MULTILINESTRING6GEOMETRY_TYPE.MULTIPOLYGON7Error codesTo import Error codes constants :from kamutils.exceptions import ERRORConstant list :Constant nameValueERROR.EXCEPTION-1ERROR.INVALID_SDO_GEOMETRY1ERROR.UNKNOW_TYPE2ERROR.UNSUPPORTED_GEOMETRY3ERROR.INVALID_GEOMETRY4LimitationsThe following geometry types are partially supported :SDO_ETYPESDO_INTERPRETATIONType (*)Limitation10Oriented pointThe point is converted but the orientation is ignored1> 1Point clusterConverted toMultipoint22Line string made up of circular arcsCircular arcs are transformed into straight line segments connecting the arc's coordinates4> 1Compound line string with vertices connected by straight line segments or by circular arcsCircular arcs are transformed into straight line segments connecting the arc's coordinates100320032Polygon made up of a connected sequence of circular arcsCircular arcs are transformed into straight line segments connecting the arc's coordinates10052005Compound polygon with some vertices connected by straight line segments and some by circular arcsCircular arcs are transformed into straight line segments connecting the arc's coordinates* FromOracle SDO_GEOMETRY Object Type - Values and Semantics in SDO_ELEM_INFOThe following geometry types are not supported by the current version. They produce an errorUNSUPPORTED_GEOMETRY.SDO_ETYPESDO_INTERPRETATIONType (*)0Geometry types not supported by Oracle Spatial23NURBS curve10033Optimized rectangle10034Circle10071Solid10073Solid of type 'optimized box'1008Multi Solid20033Optimized rectangle20034CircleOther utilitiesRangeSetRangeSetcreates a set of intervals to categorize values in different levels.RangeSetsupport numeric values for ranges.Example :from kamutils.analyse import RangeSet

s = RangeSet(1,2,5)
print('RangeSet(1,2,5)','\n')
print("Levels :")
for (level,range) in s:
	print(level,':',range.label)
print()
for v in [1, 1.5, 2, 2.1, 12]:
	r = s.level(v)
	print("Level of {:>4}".format(v),":",r,"      ",s.label(r))
print()
print("Number of intervals :",len(s))
print("Last level :",s.last_level(),'\n')

# ranges can be defined with just the upper bound of each interval like above
# or with lower and upper bounds defined, or a combination or both
s = RangeSet(1,(2,4),5,6,7,(7,9),11)
print('RangeSet(1,(2,4),5,6,7,(7,9),11)','\n')
print("Levels :")
# RangetSet is iterable. ranges are always enumerated in ascending order
for (level,range) in s:
	print(level,':',range.label)
print()
for v in [0, 1.5, 2]:
	r = s.level(v)
	print("Level of {:>4}".format(v),":",r,"      ",s.label(r))
print()
print("Number of intervals :",len(s))
print("Last level :",s.last_level(),'\n')

print("Content of this range set :")
print(s)output :RangeSet(1,2,5) 

Levels :
0 : <= 1
1 : 1]..2]
2 : 2]..5]
3 : > 5

Level of    1 : 0        <= 1
Level of  1.5 : 1        1]..2]
Level of    2 : 1        1]..2]
Level of  2.1 : 2        2]..5]
Level of   12 : 3        > 5

Number of intervals : 4
Last level : 3 

RangeSet(1,(2,4),5,6,7,(7,9),11) 

Levels :
0 : <= 1
1 : 1]..[2
2 : [2..4]
3 : 4]..5]
4 : 5]..6]
5 : 6]..7]
6 : 7]..9]
7 : 9]..11]
8 : > 11

Level of    0 : 0        <= 1
Level of  1.5 : 1        1]..[2
Level of    2 : 2        [2..4]

Number of intervals : 9
Last level : 8 

Content of this range set :
0:{'lower_bound': None, 'ex_lower_bound': False, 'upper_bound': 1, 'ex_upper_bound': False, 'label': '<= 1', 'custom_label': False}
1:{'lower_bound': 1, 'ex_lower_bound': True, 'upper_bound': 2, 'ex_upper_bound': True, 'label': '1]..[2', 'custom_label': False}
2:{'lower_bound': 2, 'ex_lower_bound': False, 'upper_bound': 4, 'ex_upper_bound': False, 'label': '[2..4]', 'custom_label': False}
3:{'lower_bound': 4, 'ex_lower_bound': True, 'upper_bound': 5, 'ex_upper_bound': False, 'label': '4]..5]', 'custom_label': False}
4:{'lower_bound': 5, 'ex_lower_bound': True, 'upper_bound': 6, 'ex_upper_bound': False, 'label': '5]..6]', 'custom_label': False}
5:{'lower_bound': 6, 'ex_lower_bound': True, 'upper_bound': 7, 'ex_upper_bound': False, 'label': '6]..7]', 'custom_label': False}
6:{'lower_bound': 7, 'ex_lower_bound': True, 'upper_bound': 9, 'ex_upper_bound': False, 'label': '7]..9]', 'custom_label': False}
7:{'lower_bound': 9, 'ex_lower_bound': True, 'upper_bound': 11, 'ex_upper_bound': False, 'label': '9]..11]', 'custom_label': False}
8:{'lower_bound': 11, 'ex_lower_bound': True, 'upper_bound': None, 'ex_upper_bound': False, 'label': '> 11', 'custom_label': False}