/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.io.sde;
import org.locationtech.jts.geom.*;
import com.esri.sde.sdk.client.*;
/**
* Reads a {@link Geometry} from an ESRI SDE Shape.
* <p>
* The SDE geometry model differs from the OGC model used by JTS.
* In particular:
* <ul>
* <li>Simple lines are read as {@link LineString}s
* <li>Inverted Polygons and Exverted Holes are read as is.
* These create invalid JTS polygons, and must be
* rectified if further operations are to be performed on them.
* </ul>
* <p>
* This class allows specifying the maximum number of coordinate dimensions to read.
* If dimensions higher than 2 are not required, it may be more efficient to not read them.
* <p>
* To use this class the ESRI SDE Java libraries must be present.
* <p>
* Currently reading measure (M) ordinates is not supported.
*
* @author Martin Davis
*
*/
public class SdeReader
{
private GeometryFactory geometryFactory;
private PrecisionModel precisionModel;
private CoordinateSequenceFactory coordSeqFact;
private int maxDimensionToRead = 2;
/**
* Creates a reader that creates geometries using the default {@link GeometryFactory}.
*/
public SdeReader() {
this(new GeometryFactory());
}
/**
* Creates a reader that creates geometries using the given {@link GeometryFactory}.
* @param geometryFactory
*/
public SdeReader(GeometryFactory geometryFactory) {
this.geometryFactory = geometryFactory;
precisionModel = geometryFactory.getPrecisionModel();
coordSeqFact = geometryFactory.getCoordinateSequenceFactory();
}
/**
* Gets the maximum number of coordinate dimensions which will be read.
*
* @return the dimension which will be read
*/
public int getDimension() {
return maxDimensionToRead;
}
/**
* Sets the maximum number of coordinate dimensions to read.
* If this is larger than the number of dimensions actually
* present in the input geometry, the higher ordinates will not
* be read, and NaN will be returned as their value.
* <p>
* The default is to read only the X and Y ordinates (dimension = 2).
*
* @param dimension the dimension to read
*/
public void setDimension(int dimension) {
this.maxDimensionToRead = dimension;
}
/**
* Reads a {@link Geometry} from a given SDE shape.
*
* @param shape the shape to read
* @return the geometry which represents the input shape
*
* @throws SeException
*/
public Geometry read(SeShape shape)
throws SeException
{
switch (shape.getType()) {
case SeShape.TYPE_POINT:
return readPoint(shape);
case SeShape.TYPE_MULTI_POINT:
return readMultiPoint(shape);
case SeShape.TYPE_LINE:
case SeShape.TYPE_SIMPLE_LINE:
return readLine(shape);
case SeShape.TYPE_MULTI_LINE:
case SeShape.TYPE_MULTI_SIMPLE_LINE:
return readMultiLine(shape);
case SeShape.TYPE_POLYGON:
return readPolygon(shape);
case SeShape.TYPE_MULTI_POLYGON:
return readMultiPolygon(shape);
}
throw new IllegalArgumentException("Shapes of type " + shape.getType() + " are not supported");
}
private Point readPoint(SeShape shape)
throws SeException
{
java.util.List list = shape.getAllPoints(SeShape.TURN_RIGHT, false); // get points and offsets
SDEPoint[] sePts = (SDEPoint[]) list.get(0);
return geometryFactory.createPoint(toCoordinates(sePts, 0, 1));
}
private MultiPoint readMultiPoint(SeShape shape)
throws SeException
{
java.util.List list = shape.getAllPoints(SeShape.TURN_RIGHT, false); // get points and offsets
SDEPoint[] sePts = (SDEPoint[]) list.get(0);
return geometryFactory.createMultiPoint(toCoordinates(sePts, 0, sePts.length));
}
private LineString readLine(SeShape shape)
throws SeException
{
java.util.List list = shape.getAllPoints(SeShape.TURN_RIGHT, false); // get points and offsets
SDEPoint[] sePts = (SDEPoint[]) list.get(0);
return geometryFactory.createLineString(toCoordinates(sePts, 0, sePts.length));
}
private MultiLineString readMultiLine(SeShape shape)
throws SeException
{
java.util.List list = shape.getAllPoints(SeShape.TURN_RIGHT, false); // get points and offsets
SDEPoint[] sePts = (SDEPoint[]) list.get(0);
int[] partOffset = (int[]) list.get(1);
LineString[] lines = new LineString[partOffset.length];
for (int i = 0; i < partOffset.length; i++) {
int end = sePts.length;
if (i < partOffset.length - 1)
end = partOffset[i + 1];
lines[i] = geometryFactory.createLineString(toCoordinates(sePts, partOffset[i], end));
}
return geometryFactory.createMultiLineString(lines);
}
private Polygon readPolygon(SeShape shape)
throws SeException
{
java.util.List list = shape.getAllPoints(SeShape.TURN_RIGHT, true); // get points and offsets
SDEPoint[] sePts = (SDEPoint[]) list.get(0);
// a polygon has only one part
int[] subPartOffset = (int[]) list.get(2);
return readPolygon(sePts, subPartOffset, 0, subPartOffset.length);
}
private Polygon readPolygon(SDEPoint[] sePts, int[] subPartOffset, int subPartStart, int subPartEnd)
throws SeException
{
int numSubParts = subPartEnd - subPartStart;
int nHoles = numSubParts - 1;
if (nHoles < 0)
nHoles = 0;
LinearRing shell = null;
LinearRing[] holes = new LinearRing[nHoles];
int holeIndex = 0;
for (int i = subPartStart; i < subPartEnd; i++) {
int end = sePts.length;
if (i < subPartEnd - 1)
end = subPartOffset[i + 1];
LinearRing ring = geometryFactory.createLinearRing(toCoordinates(sePts, subPartOffset[i], end));
if (shell == null) {
shell = ring;
}
else {
holes[holeIndex++] = ring;
}
}
return geometryFactory.createPolygon(shell, holes);
}
private MultiPolygon readMultiPolygon(SeShape shape)
throws SeException
{
java.util.List list = shape.getAllPoints(SeShape.TURN_RIGHT, true); // get points and offsets
SDEPoint[] sePts = (SDEPoint[]) list.get(0);
int[] partOffset = (int[]) list.get(1);
int[] subPartOffset = (int[]) list.get(2);
Polygon[] polys = new Polygon[partOffset.length];
for (int i = 0; i < partOffset.length; i++) {
int subPartEnd = subPartOffset.length;
if (i + 1 < partOffset.length) {
subPartEnd = partOffset[i + 1];
}
polys[i] = readPolygon(sePts, subPartOffset, partOffset[i], subPartEnd);
}
return geometryFactory.createMultiPolygon(polys);
}
private void readCoordinate(SDEPoint p, CoordinateSequence seq, int index)
throws SeException
{
seq.setOrdinate(index, 0, precisionModel.makePrecise(p.getX()));
seq.setOrdinate(index, 1, precisionModel.makePrecise(p.getY()));
// only read the Z dim if requested and present
if (maxDimensionToRead >= 3 && p.is3D()) {
seq.setOrdinate(index, 2, p.getZ());
}
}
private CoordinateSequence toCoordinates(SDEPoint[] sePts, int start, int end)
throws SeException
{
int size = end - start;
CoordinateSequence seq = coordSeqFact.create(size, maxDimensionToRead);
int index = 0;
for (int i = start; i < end; i++) {
readCoordinate(sePts[i], seq, index);
index++;
}
return seq;
}
}