![JAR search and dependency download from the Maven repository](/logo.png)
com.vividsolutions.jts.io.oracle.OraWriter Maven / Gradle / Ivy
/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
/*
* Geotools2 - OpenSource mapping toolkit
* http://geotools.org
* (C) 2003, Geotools Project Managment Committee (PMC)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
package com.vividsolutions.jts.io.oracle;
import java.sql.SQLException;
import java.util.*;
import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.util.Assert;
import oracle.jdbc.OracleConnection;
import oracle.sql.*;
/**
* Translates a JTS Geometry into an Oracle STRUCT representing an MDSYS.SDO_GEOMETRY
object.
* Supports writing all JTS geometry types into an equivalent Oracle representation.
*
* To write an Oracle STRUCT
a connection to an Oracle instance with access to the definition of the MDSYS.SDO_GEOMETRY
* type is required. Oracle SDO_GEOMETRY
SQL strings may be written without a connection, however.
*
* By default, a single {@link Point} is written using the optimized SDO_POINT_TYPE
attribute.
* This can be overridden to use the (less compact) SDO_ELEM_INFO/SDOORDINATES
representation
* by using {@link #setOptimizePoint(boolean)}.
*
* By default, rectangular polygons are written as regular 5-point polygons.
* This can be changed to use the optimized RECTANGLE points
* by using {@link #setOptimizeRectangle(boolean)}.
* Note that RECTANGLEs do not support LRS Measure ordinate values.
* Also, this class only writes RECTANGLEs for polygons containing a single ring (i.e. the shell).
*
* Oracle cannot represent {@link MultiPolygon}s or {@link MultiLineString}s directly as elements
* of a {@link GeometryCollection}. Instead, their components are written individually.
* {@link MultiPoint}s are represented directly, however.
*
* The dimension of the output SDO_GEOMETRY
is determined as follows:
*
* - by default, the dimension matches that of the input
*
- currently the coordinate dimension of the input is determined by inspecting a sample coordinate.
* If the Z value is
NaN
, the coordinate dimension is assumed to be 2.
* (In the future this will be determined from the underlying {@link CoordinateSequence}s.
* - the dimension can be set explicitly by the {@link #setDimension(int)} method.
* This allows forcing Z output even if the Z values are
NaN
.
* Conversely, if Z values are present this allows forcing 2D output.
*
*
* LIMITATIONS
*
* - Since JTS does not support Measures, they cannot be written.
* (A future release could allow forcing interpreting Z as M, or else providing a fixed M value).
*
*
* @author Martin Davis
* @author David Zwiers, Vivid Solutions.
*/
public class OraWriter
{
/**
* A connection providing access to the required type definitions
*/
private OracleConnection connection;
/**
* The maximum output dimension to write
*/
private int outputDimension = OraGeom.NULL_DIMENSION;
/**
* The default SRID to write
*/
private int srid = OraGeom.SRID_NULL;
private boolean isOptimizeRectangle = false;
private boolean isOptimizePoint = true;
/**
* Creates a writer for Oracle geometry formats.
*
* The output dimension will be whatever the dimension of the input is.
*/
public OraWriter()
{
}
/**
* Creates a writer for Oracle geometry formats,
* specifying the maximum output dimension.
*
* @param outputDimension the coordinate dimension to use for the output
*/
public OraWriter(int outputDimension)
{
this.outputDimension = outputDimension;
}
/**
* Creates a writer using a valid Oracle connection.
*
* To simplify connection resource handling, the connection should be
* provided in the {@link #write(Geometry, OracleConnection)} method.
* Accordingly, this constructor has been deprecated.
*
* The connection should have sufficient privileges to view the description of the MDSYS.SDO_GEOMETRY type.
*
* The output dimension will be whatever the dimension of the input is.
*
* @param con a valid Oracle connection
* @deprecated use {@link #OraWriter()} instead
*/
public OraWriter(OracleConnection con)
{
this.connection = con;
}
/**
* Creates a writer using a valid Oracle connection,
* and specifying the maximum output dimension.
*
* To simplify connection resource handling, the connection should be
* provided in the {@link #write(Geometry, OracleConnection)} method.
* Accordingly, this constructor has been deprecated.
*
* The connection should have sufficient privileges to view the description of the MDSYS.SDO_GEOMETRY type.
*
* @param con a valid Oracle connection
* @param outputDimension the coordinate dimension to use for the output
* @deprecated use {@link #OraWriter(int)} instead
*/
public OraWriter(OracleConnection con, int outputDimension)
{
this.connection = con;
this.outputDimension = outputDimension;
}
/**
* Sets the coordinate dimension for the created Oracle geometries.
*
* @param outputDimension
* the coordinate dimension to use for the output
*/
public void setDimension(int outputDimension)
{
if (outputDimension < 2)
throw new IllegalArgumentException("Output dimension must be >= 2");
this.outputDimension = outputDimension;
}
/**
* Forces geometries to be written using the specified SRID.
* This is useful in two cases:
*
* - to avoid using the native geometry's SRID
*
- to ensure an entire table is written using a fixed SRID.
*
*
* @param srid the srid to use
*/
public void setSRID(int srid)
{
this.srid = srid;
}
/**
* Sets whether rectangle polygons should be written using the
* optimized 4-coordinate RECTANGLE format
* (ETYPE=1003, INTERPRETATION=3).
* If this option is false, rectangles are written as 5-coordinate polygons.
* The default setting is false
.
*
* @param isOptimizeRectangle whether to optimize rectangle writing
*/
public void setOptimizeRectangle(boolean isOptimizeRectangle)
{
this.isOptimizeRectangle = isOptimizeRectangle;
}
/**
* Sets whether points should be written using the
* optimized SDO_POINT_TYPE format.
* If this option is false
, points are written using the SDO_ORDINATES attribute.
* The default setting is true
.
*
* @param isOptimizePoint whether to optimize point writing
*/
public void setOptimizePoint(boolean isOptimizePoint)
{
this.isOptimizePoint = isOptimizePoint;
}
/**
* Converts a {@link Geometry} into an Oracle MDSYS.SDO_GEOMETRY STRUCT.
*
* Although invalid geometries may be encoded, and inserted into an Oracle DB,
* this is not recommended. It is the responsibility of the user to ensure the
* geometry is valid prior to calling this method.
*
* The SRID of the created SDO_GEOMETRY is the SRID defined explicitly for the writer, if any;
* otherwise it is the SRID contained in the input geometry.
* The caller should ensure the the SRID is valid for the intended use,
* since an incorrect SRID may cause indexing exceptions during an
* INSERT or UPDATE.
*
* When a null Geometry is passed in, a non-null, empty SDO_GEOMETRY STRUCT is returned.
* Therefore, inserting the output of the writer into a
* table will never result in NULL insertions.
* To pass a NULL Geometry into an Oracle SDO_GEOMETRY-valued parameter using JDBC, use
*
* java.sql.CallableStatement.setNull(index, java.sql.Types.STRUCT, "MDSYS.SDO_GEOMETRY").
*
*
* @param geom the geometry to encode
* @return a Oracle MDSYS.SDO_GEOMETRY STRUCT representing the geometry
* @throws SQLException if an encoding error was encountered
* @deprecated
*/
public STRUCT write(Geometry geom) throws SQLException
{
return write(geom, connection);
}
/**
* Converts a {@link Geometry} into an Oracle MDSYS.SDO_GEOMETRY STRUCT.
*
* Although invalid geometries may be encoded, and inserted into an Oracle DB,
* this is not recommended. It is the responsibility of the user to ensure the
* geometry is valid prior to calling this method.
*
* The SRID of the created SDO_GEOMETRY is the SRID defined explicitly for the writer, if any;
* otherwise it is the SRID contained in the input geometry.
* The caller should ensure the the SRID is valid for the intended use,
* since an incorrect SRID may cause indexing exceptions during an
* INSERT or UPDATE.
*
* When a null Geometry is passed in, a non-null, empty SDO_GEOMETRY STRUCT is returned.
* Therefore, inserting the output of the writer into a
* table will never result in NULL insertions.
* To pass a NULL Geometry into an Oracle SDO_GEOMETRY-valued parameter using JDBC, use
*
* java.sql.CallableStatement.setNull(index, java.sql.Types.STRUCT, "MDSYS.SDO_GEOMETRY").
*
*
* @param geom the geometry to encode
* @return a Oracle MDSYS.SDO_GEOMETRY STRUCT representing the geometry
* @throws SQLException if an encoding error was encountered
*/
public STRUCT write(Geometry geom, OracleConnection connection) throws SQLException
{
// this line may be problematic ... for v9i and later need to revisit.
// was this ... does not work for 9i
// if( geom == null) return toSTRUCT( null, DATATYPE );
if (geom == null || geom.isEmpty() || geom.getCoordinate() == null)
return createEmptySDOGeometry(connection);
OraGeom oraGeom = createOraGeom(geom);
STRUCT SDO_POINT = null;
ARRAY SDO_ELEM_INFO = null;
ARRAY SDO_ORDINATES = null;
if (oraGeom.point == null) {
SDO_ELEM_INFO = OraUtil.toARRAY(oraGeom.elemInfo, OraGeom.TYPE_ELEM_INFO_ARRAY,
connection);
SDO_ORDINATES = OraUtil.toARRAY(oraGeom.ordinates, OraGeom.TYPE_ORDINATE_ARRAY,
connection);
}
else { // Point Optimization
Datum data[] = new Datum[] {
OraUtil.toNUMBER(oraGeom.point[0]),
OraUtil.toNUMBER(oraGeom.point[1]),
OraUtil.toNUMBER(oraGeom.point[2]), };
SDO_POINT = OraUtil.toSTRUCT(data, OraGeom.TYPE_POINT_TYPE, connection);
}
NUMBER SDO_GTYPE = new NUMBER(oraGeom.gType);
NUMBER SDO_SRID = oraGeom.srid == OraGeom.SRID_NULL ? null : new NUMBER(oraGeom.srid);
Datum sdoGeometryComponents[] = new Datum[] {
SDO_GTYPE,
SDO_SRID,
SDO_POINT,
SDO_ELEM_INFO,
SDO_ORDINATES };
return OraUtil.toSTRUCT(sdoGeometryComponents, OraGeom.TYPE_GEOMETRY, connection);
}
/**
* Writes a Geometry in Oracle SDO_GEOMETRY SQL literal format.
*
* Examples of output are:
*
* SDO_GEOMETRY(2001,NULL,NULL,SDO_ELEM_INFO_ARRAY(1,1,1),SDO_ORDINATE_ARRAY(50,50))
* SDO_GEOMETRY(3001,NULL,SDO_POINT_TYPE(50,50,100,),NULL,NULL)
* SDO_GEOMETRY(3006,8307,NULL,SDO_ELEM_INFO_ARRAY(1,2,1, 7,2,1),SDO_ORDINATE_ARRAY(0,0,2, 50,50,100, 10,10,12, 150,150,110))
*
*
* @param geom the Geometry to write
* @return a string representing the geometry as an SDO_GEOMETRY literal
*/
public String writeSQL(Geometry geom)
{
if (geom == null) return OraGeom.SQL_NULL;
OraGeom oraGeom = createOraGeom(geom);
return oraGeom.toString();
}
private STRUCT createEmptySDOGeometry(OracleConnection connection) throws SQLException {
return OraUtil.toSTRUCT(new Datum[5], OraGeom.TYPE_GEOMETRY, connection);
}
/**
* Creates an {@link OraGeom} structure corresponding to the Oracle SDO_GEOMETRY
* attributes representing the given Geometry.
* This allows disconnected testing, since no Oracle types are accessed.
*
* @param geom the non-null, non-empty Geometry to write
* @return an OraGeom structure
*/
OraGeom createOraGeom(Geometry geom)
{
int gtype = gType(geom);
int srid = this.srid == OraGeom.SRID_NULL ? geom.getSRID() : this.srid;
double[] point = null;
int elemInfo[] = null;
double[] ordinates = null;
// if geometry ordinate data should be represented by SDO_ORDINATES array
if (isEncodeAsPointType(geom)) {
point = pointOrdinates(geom);
}
else {
int dim = dimension(geom);
List elemTriplets = new ArrayList();
List ordGeoms = new ArrayList();
int lastOrdOffset = writeElement(geom, dim, 1, elemTriplets, ordGeoms);
elemInfo = flattenTriplets(elemTriplets);
ordinates = writeGeometryOrdinates(elemTriplets, ordGeoms, lastOrdOffset - 1, dim);
}
OraGeom oraGeom = new OraGeom(gtype, srid, point, elemInfo, ordinates);
return oraGeom;
}
/**
* Extracts ordinate data for SDO_POINT_TYPE for Point geometries.
* null
is returned
* for all non-Point geometries, or for LRS points.
* This cannot be used for LRS coordinates.
* Subclasses may wish to repress this method and force Points to be
* represented using SDO_ORDINATES.
*
* @param geom the geometry providing the ordinates
* @return double[] the point ordinates
*/
private double[] pointOrdinates(Geometry geom)
{
Point point = (Point) geom;
Coordinate coord = point.getCoordinate();
return new double[] { coord.x, coord.y, coord.z };
}
/**
* Writes each geometry element which will appear in the output,
* by recursing through the input geometry,
* and identifying each element and how it will
* appear in the output elemInfo array.
* For each element the relevant geometry component
* is recorded as well, to allow the ordinates
* array to be written from them subsequently.
* The total length of the ordinate array is summed
* during this process as well (which also allows determining startingOffsets).
*
* @param geom
* @param dim
* @param offset
* @param elemTriplets
* @param ordGeoms
* @return the final startingOffset
*/
private int writeElement(Geometry geom, int dim, int offset, List elemTriplets, List ordGeoms)
{
int interp;
int geomType = OraGeom.geomType(geom);
switch (geomType) {
case OraGeom.GEOM_TYPE.POINT:
// full point encoding - optimized one has been done earlier if possible
Point point = (Point) geom;
elemTriplets.add(triplet(offset, OraGeom.ETYPE.POINT, OraGeom.INTERP.POINT));
ordGeoms.add(point);
return offset + dim;
case OraGeom.GEOM_TYPE.MULTIPOINT:
MultiPoint points = (MultiPoint) geom;
int nPts = points.getNumGeometries();
// this works for nPts >= 1 (0 has already been handled)
elemTriplets.add(triplet(offset, OraGeom.ETYPE.POINT, nPts));
ordGeoms.add(points);
return offset + dim * nPts;
case OraGeom.GEOM_TYPE.LINE:
LineString line = (LineString) geom;
elemTriplets.add(triplet(offset, OraGeom.ETYPE.LINE, OraGeom.INTERP.LINESTRING));
ordGeoms.add(line);
return offset + dim * line.getNumPoints();
case OraGeom.GEOM_TYPE.MULTILINE:
MultiLineString lines = (MultiLineString) geom;
for (int i = 0; i < lines.getNumGeometries(); i++) {
LineString lineElem = (LineString) lines.getGeometryN(i);
offset = writeElement(lineElem, dim, offset, elemTriplets, ordGeoms);
}
return offset;
case OraGeom.GEOM_TYPE.POLYGON:
Polygon polygon = (Polygon) geom;
// shell
LineString ring = polygon.getExteriorRing();
interp = isWriteAsRectangle(polygon) ? OraGeom.INTERP.RECTANGLE : OraGeom.INTERP.POLYGON;
elemTriplets.add(triplet(offset, OraGeom.ETYPE.POLYGON_EXTERIOR, interp));
ordGeoms.add(ring);
if (interp == OraGeom.INTERP.RECTANGLE) {
offset += 4;
}
else {
offset += dim * ring.getNumPoints();
}
// holes
int holes = polygon.getNumInteriorRing();
for (int i = 0; i < holes; i++) {
ring = polygon.getInteriorRingN(i);
elemTriplets.add(triplet(offset, OraGeom.ETYPE.POLYGON_INTERIOR, OraGeom.INTERP.POLYGON));
ordGeoms.add(ring);
offset += dim * ring.getNumPoints();
}
return offset;
case OraGeom.GEOM_TYPE.MULTIPOLYGON:
MultiPolygon polys = (MultiPolygon) geom;
Polygon poly;
for (int i = 0; i < polys.getNumGeometries(); i++) {
poly = (Polygon) polys.getGeometryN(i);
offset = writeElement(poly, dim, offset, elemTriplets, ordGeoms);
}
return offset;
case OraGeom.GEOM_TYPE.COLLECTION:
GeometryCollection geoms = (GeometryCollection) geom;
for (int i = 0; i < geoms.getNumGeometries(); i++) {
geom = geoms.getGeometryN(i);
offset = writeElement(geom, dim, offset, elemTriplets, ordGeoms);
}
return offset;
}
throw new IllegalArgumentException("Cannot encode JTS "
+ geom.getGeometryType() + " as SDO_ELEM_INFO "
+ "(Limited to Point, Line, Polygon, GeometryCollection, MultiPoint,"
+ " MultiLineString and MultiPolygon)");
}
private static int[] triplet(int sOffset, int etype, int interp)
{
return new int[] { sOffset, etype, interp };
}
private int[] flattenTriplets(List elemTriplets)
{
int[] elemInfo = new int[3 * elemTriplets.size()];
int eiIndex = 0;
for (int i = 0; i < elemTriplets.size(); i++) {
int[] triplet = (int[]) elemTriplets.get(i);
for (int ii = 0; ii < 3; ii++) {
elemInfo[eiIndex++] = triplet[ii];
}
}
return elemInfo;
}
/**
* Writes the ordinate values extracted from each element Geometry
* into a double array.
* This optimizes memory usage by only allocating the single
* double array required to pass the ordinates to the Oracle STRUCT.
*
* @param elemTriplets
* @param ordGeoms
* @param ordSize
* @param dim
* @return the final ordinate array
*/
private double[] writeGeometryOrdinates(List elemTriplets, List ordGeoms, int ordSize, int dim)
{
double[] ords = new double[ordSize];
int ordIndex = 0;
for (int ielem = 0; ielem < elemTriplets.size(); ielem++) {
int[] triplet = (int[]) elemTriplets.get(ielem);
int startOffset = triplet[0];
//verify startOffset is same as ordIndex
Assert.isTrue(startOffset == ordIndex + 1,
"ElemInfo computed startingOffset does not match actual ordinates position");
int elemType = triplet[1];
int interp = triplet[2];
Geometry geom = (Geometry) ordGeoms.get(ielem);
switch (elemType) {
case OraGeom.ETYPE.POINT:
if (interp == 1) {
ordIndex = writeOrds(((Point) geom).getCoordinateSequence(), dim, ords, ordIndex);
}
else {
// must be > 1 - write MultiPoint
ordIndex = writeOrds((MultiPoint) geom, dim, ords, ordIndex);
}
break;
case OraGeom.ETYPE.LINE:
ordIndex = writeOrds(((LineString) geom).getCoordinateSequence(), dim, ords, ordIndex);
break;
case OraGeom.ETYPE.POLYGON_EXTERIOR:
if (interp == OraGeom.INTERP.RECTANGLE) {
ordIndex = writeRectangleOrds(geom, dim, ords, ordIndex);
}
else {
ordIndex = writeOrdsOriented(((LineString) geom).getCoordinateSequence(), dim, ords, ordIndex, true);
}
break;
case OraGeom.ETYPE.POLYGON_INTERIOR:
ordIndex = writeOrdsOriented(((LineString) geom).getCoordinateSequence(), dim, ords, ordIndex, false);
break;
}
}
return ords;
}
/**
* Writes ordinates in the orientation
* specified by the isWriteCCW CCW flag.
* Coordinates are reversed if necessary.
*
* @param seq the coordinates to write
* @param dim the output dimension required
* @param ordData the ordinates array
* @param ordIndex the starting index in the ordinates array
* @param isWriteCCW true if the ordinates should be written in CCW orientation, false if CW
* @return the next index to write in the ordinates array
*/
private int writeOrdsOriented(CoordinateSequence seq, int dim,
double[] ordData, int ordIndex, boolean isWriteCCW)
{
Coordinate[] coords = seq.toCoordinateArray();
//TODO: add method to CGAlgorithms to compute isCCW for CoordinateSequences
boolean isCCW = CGAlgorithms.isCCW(coords);
if (isCCW != isWriteCCW) {
return writeOrdsReverse(seq, dim, ordData, ordIndex);
}
return writeOrds(seq, dim, ordData, ordIndex);
}
private int writeOrdsReverse(CoordinateSequence seq, int dim, double[] ordData, int ordIndex)
{
int nCoord = seq.size();
for (int i = nCoord-1; i >= 0; i--) {
for (int id = 0; id < dim; id++) {
ordData[ordIndex++] = seq.getOrdinate(i, id);
}
}
return ordIndex;
}
private int writeOrds(CoordinateSequence seq, int dim, double[] ordData, int ordIndex)
{
int nCoord = seq.size();
for (int i = 0; i < nCoord; i++) {
for (int id = 0; id < dim; id++) {
ordData[ordIndex++] = seq.getOrdinate(i, id);
}
}
return ordIndex;
}
private int writeOrds(MultiPoint geom, int dim, double[] ordData, int ordIndex)
{
int nGeom = geom.getNumGeometries();
for (int i = 0; i < nGeom; i++) {
CoordinateSequence seq = ((Point) geom.getGeometryN(i)).getCoordinateSequence();
for (int id = 0; id < dim; id++) {
ordData[ordIndex++] = seq.getOrdinate(0, id);
}
}
return ordIndex;
}
private int writeRectangleOrds(Geometry ring, int dim, double[] ordData, int ordIndex)
{
Envelope e = ring.getEnvelopeInternal();
ordData[ordIndex++] = e.getMinX();
ordData[ordIndex++] = e.getMinY();
ordData[ordIndex++] = e.getMaxX();
ordData[ordIndex++] = e.getMaxY();
return ordIndex;
}
/**
* Tests if a polygon
can be written aa a RECTANGLE.
* Rectangles are only supported without a SRID!
*
* @param polygon
* @return true
if polygon is SRID==NULL and a rectangle
*/
private boolean isWriteAsRectangle(Polygon polygon) {
if (! isOptimizeRectangle) return false;
if (lrsDim(polygon) != 0) {
// cannot support LRS on a rectangle
return false;
}
return polygon.isRectangle();
}
private boolean isEncodeAsPointType(Geometry geom)
{
if (! isOptimizePoint) return false;
if (geom instanceof Point && (lrsDim(geom) == 0) && outputDimension <= 3)
return true;
// Geometry type is not appropriate for SDO_POINT_TYPE
return false;
}
/**
* Produce SDO_GTYPE code for input Geometry.
*
* @param geom
* @return SDO_GTYPE code
*/
private int gType(Geometry geom)
{
return OraGeom.gType(dimension(geom), lrsDim(geom), OraGeom.geomType(geom));
}
/**
* Return dimension of output coordinates (either 2,3 or 4),
* respecting the explicit output dimension (if any).
*
* @param geom
* @return coordinate dimension number
*/
private int dimension(Geometry geom) {
if (outputDimension != OraGeom.NULL_DIMENSION)
return outputDimension;
//TODO: check dimension of a geometry CoordinateSequence to determine dimension
int d = Double.isNaN(geom.getCoordinate().z) ? 2 : 3;
return d;
}
/**
* Return LRS dimension as defined by SDO_GTYPE (either 3,4 or 0).
*
* @param geom
* @return LRS dimension
*/
private int lrsDim(Geometry geom) {
//TODO: implement measure support when available
return 0;
}
}