- plugged a leak in GEOS2POSTGIS converter
- jdbc2: added BETA support for jts geometry classes
- jdbc2: Skip known-to-fail tests against older PostGIS servers.
+ - jdbc2: Fixed handling of measured geometries in EWKT.
PostGIS 1.0.0RC2
2005/01/26
import org.postgis.Geometry;
import org.postgis.PGgeometry;
+import org.postgresql.PGConnection;
import org.postgresql.util.PGtokenizer;
import java.sql.Connection;
*/
public static final String[][] testset = new String[][]{
{
- ALL,
+ ALL, // 2D
+ "POINT(10 10)"},
+ {
+ ALL, // 3D with 3rd coordinate set to 0
+ "POINT(10 10 0)"},
+ {
+ ALL, // 3D
"POINT(10 10 20)"},
+ {
+ ONLY10, // 2D + Measures
+ "POINTM(10 10 20)"},
+ {
+ ONLY10, // 3D + Measures
+ "POINT(10 10 20 30)"},
+ {
+ ALL,
+ "MULTIPOINT(11 12, 20 20)"},
{
ALL,
- "MULTIPOINT(10 10 10, 20 20 20)"},
+ "MULTIPOINT(11 12 13, 20 20 20)"},
+ {
+ ONLY10,
+ "MULTIPOINTM(11 12 13, 20 20 20)"},
+ {
+ ONLY10,
+ "MULTIPOINT(11 12 13 14,20 20 20 20)"},
+ {
+ ALL,
+ "LINESTRING(10 10,20 20,50 50,34 34)"},
+ {
+ ALL,
+ "LINESTRING(10 10 20,20 20 20,50 50 50,34 34 34)"},
+ {
+ ONLY10,
+ "LINESTRINGM(10 10 20,20 20 20,50 50 50,34 34 34)"},
+ {
+ ONLY10,
+ "LINESTRING(10 10 20 20,20 20 20 20,50 50 50 50,34 34 34 50)"},
{
ALL,
- "LINESTRING(10 10 20,20 20 20, 50 50 50, 34 34 34)"},
+ "POLYGON((10 10,20 10,20 20,20 10,10 10),(5 5,5 6,6 6,6 5,5 5))"},
{
ALL,
"POLYGON((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))"},
+ {
+ ONLY10,
+ "POLYGONM((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))"},
+ {
+ ONLY10,
+ "POLYGON((10 10 0 7,20 10 0 7,20 20 0 7,20 10 0 7,10 10 0 7),(5 5 0 7,5 6 0 7,6 6 0 7,6 5 0 7,5 5 0 7))"},
+ {
+ ALL,
+ "MULTIPOLYGON(((10 10,20 10,20 20,20 10,10 10),(5 5,5 6,6 6,6 5,5 5)),((10 10,20 10,20 20,20 10,10 10),(5 5,5 6,6 6,6 5,5 5)))"},
{
ALL,
"MULTIPOLYGON(((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)),((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)))"},
+ {
+ ONLY10,
+ "MULTIPOLYGONM(((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)),((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)))"},
+ {
+ ONLY10,
+ "MULTIPOLYGON(((10 10 0 7,20 10 0 7,20 20 0 7,20 10 0 7,10 10 0 7),(5 5 0 7,5 6 0 7,6 6 0 7,6 5 0 7,5 5 0 7)),((10 10 0 7,20 10 0 7,20 20 0 7,20 10 0 7,10 10 0 7),(5 5 0 7,5 6 0 7,6 6 0 7,6 5 0 7,5 5 0 7)))"},
+ {
+ ALL,
+ "MULTILINESTRING((10 10,20 10,20 20,20 10,10 10),(5 5,5 6,6 6,6 5,5 5))"},
{
ALL,
- "MULTILINESTRING((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))"},
+ "MULTILINESTRING((10 10 5,20 10 5,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))"},
+ {
+ ONLY10,
+ "MULTILINESTRINGM((10 10 7,20 10 7,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))"},
+ {
+ ONLY10,
+ "MULTILINESTRING((10 10 0 7,20 10 0 7,20 20 0 7,20 10 0 7,10 10 0 7),(5 5 0 7,5 6 0 7,6 6 0 7,6 5 0 7,5 5 0 7))"},
+ {
+ ALL,
+ "GEOMETRYCOLLECTION(POINT(10 10),POINT(20 20))"},
{
ALL,
"GEOMETRYCOLLECTION(POINT(10 10 20),POINT(20 20 20))"},
+ {
+ ONLY10,
+ "GEOMETRYCOLLECTIONM(POINT(10 10 20),POINT(20 20 20))"},
+ {
+ ONLY10,
+ "GEOMETRYCOLLECTION(POINT(10 10 20 7),POINT(20 20 20 7))"},
{
ALL,
"GEOMETRYCOLLECTION(LINESTRING(10 10 20,20 20 20, 50 50 50, 34 34 34),LINESTRING(10 10 20,20 20 20, 50 50 50, 34 34 34))"},
ONLY10, // Cannot be parsed by 0.X servers
"GEOMETRYCOLLECTION(MULTIPOINT(10 10 10, 20 20 20),MULTIPOINT(10 10 10, 20 20 20))"},
{
- EQUAL10, // PostGIs 0.X "flattens" this geometry, so it is not equal after reparsing.
+ EQUAL10, // PostGIs 0.X "flattens" this geometry, so it is not equal
+ // after reparsing.
"GEOMETRYCOLLECTION(MULTILINESTRING((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)))"},
{
- EQUAL10,// PostGIs 0.X "flattens" this geometry, so it is not equal after reparsing.
+ EQUAL10,// PostGIs 0.X "flattens" this geometry, so it is not equal
+ // after reparsing.
"GEOMETRYCOLLECTION(MULTIPOLYGON(((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)),((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))),MULTIPOLYGON(((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0)),((10 10 0,20 10 0,20 20 0,20 10 0,10 10 0),(5 5 0,5 6 0,6 6 0,6 5 0,5 5 0))))"},
{
ALL,
for (int i = 0; i < dburls.getSize(); i++) {
System.out.println("Creating JDBC connection to " + dburls.getToken(i));
conns[i] = connect(dburls.getToken(i), dbuser, dbpass);
+ ((PGConnection) conns[i]).addDataType("geometry", "org.postgis.PGgeometry");
}
System.out.println("Performing tests...");
this.subgeoms = geoms;
if (geoms.length > 0) {
dimension = geoms[0].dimension;
+ haveMeasure = geoms[0].haveMeasure;
} else {
dimension = 0;
}
* geometry specific equals implementation
*/
public boolean equals(Geometry other) {
- return ((other != null) && (this.dimension == other.dimension) && (this.type == other.type)
- && (this.srid == other.srid) && (this.haveMeasure == other.haveMeasure)
- && other.getClass().equals(this.getClass()) && this.equalsintern(other));
+ boolean firstline = (other != null) && (this.dimension == other.dimension) && (this.type == other.type);
+ boolean sridequals = (this.srid == other.srid);
+ boolean measEquals = (this.haveMeasure == other.haveMeasure);
+ boolean secondline = sridequals && measEquals;
+ boolean classequals = other.getClass().equals(this.getClass());
+ boolean equalsintern = this.equalsintern(other);
+ boolean result = firstline
+ && secondline
+ && classequals && equalsintern;
+ return result;
}
/**
sb.append(srid);
sb.append(';');
}
- outerWKT(sb);
+ outerWKT(sb, true);
return sb.toString();
}
* Render the WKT version of this Geometry (without SRID) into the given
* StringBuffer.
*/
- public void outerWKT(StringBuffer sb) {
+ public void outerWKT(StringBuffer sb, boolean putM) {
sb.append(typestring);
- if (haveMeasure && dimension == 2) {
+ if (putM && haveMeasure && dimension == 2) {
sb.append('M');
}
mediumWKT(sb);
}
+ public final void outerWKT(StringBuffer sb) {
+ outerWKT(sb, true);
+ }
+
/**
* Render the WKT without the type name, but including the brackets into the
* StringBuffer
}
public GeometryCollection(String value) throws SQLException {
+ this(value, false);
+ }
+
+ public GeometryCollection(String value, boolean haveM) throws SQLException {
this();
value = value.trim();
if (value.equals(EmptyColl)) {
//Do nothing
} else if (value.startsWith(GeoCollID)) {
- PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value.substring(
- GeoCollID.length()).trim()), ',');
+ int pfxlen = typestring.length();
+ if (value.charAt(pfxlen) == 'M') {
+ pfxlen += 1;
+ haveM = true;
+ }
+ value = value.substring(pfxlen).trim();
+
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value), ',');
int ngeoms = t.getSize();
subgeoms = new Geometry[ngeoms];
for (int p = 0; p < ngeoms; p++) {
- subgeoms[p] = PGgeometry.geomFromString(t.getToken(p));
+ subgeoms[p] = PGgeometry.geomFromString(t.getToken(p), haveM);
}
dimension = subgeoms[0].dimension;
+ haveMeasure = subgeoms[0].haveMeasure;
} else {
throw new SQLException("postgis.geocollection");
}
}
protected void innerWKT(StringBuffer SB) {
- subgeoms[0].outerWKT(SB);
+ subgeoms[0].outerWKT(SB, false);
for (int i = 1; i < subgeoms.length; i++) {
SB.append(',');
- subgeoms[i].outerWKT(SB);
+ subgeoms[i].outerWKT(SB, false);
}
}
}
\ No newline at end of file
super(LINESTRING, value);
}
+ public LineString(String value, boolean haveM) throws SQLException {
+ super(LINESTRING, value, haveM);
+ }
+
public LineString reverse() {
Point[] points = this.getPoints();
int l = points.length;
* @param value Definition of this ring in the PostGIS string format.
*/
public LinearRing(String value) throws SQLException {
+ this(value, false);
+ }
+
+ /**
+ * @param value The text representation of this LinearRing
+ * @param haveM Hint whether we have a measure. This is given to us by other
+ * "parent" Polygon, and is passed further to our parent.
+ */
+
+ protected LinearRing(String value, boolean haveM) throws SQLException {
super(LINEARRING);
PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value.trim()), ',');
int npoints = t.getSize();
Point[] points = new Point[npoints];
for (int p = 0; p < npoints; p++) {
- points[p] = new Point(t.getToken(p));
+ points[p] = new Point(t.getToken(p), haveM);
}
- dimension = points[0].dimension;
+ this.dimension = points[0].dimension;
+ // fetch haveMeasure from subpoint because haveM does only work with
+ // 2d+M, not with 3d+M geometries
+ this.haveMeasure = points[0].haveMeasure;
this.subgeoms = points;
}
}
\ No newline at end of file
}
public MultiLineString(String value) throws SQLException {
+ this(value, false);
+ }
+
+ protected MultiLineString(String value, boolean haveM) throws SQLException {
this();
- value = value.trim();
- if (value.indexOf("MULTILINESTRING") == 0) {
- PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value.substring(15).trim()), ',');
+ value = value.trim();
+ if (value.indexOf(typestring) == 0) {
+ int pfxlen = typestring.length();
+ if (value.charAt(pfxlen) == 'M') {
+ pfxlen += 1;
+ haveM = true;
+ }
+ value = value.substring(pfxlen).trim();
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value), ',');
int nlines = t.getSize();
subgeoms = new LineString[nlines];
for (int p = 0; p < nlines; p++) {
- subgeoms[p] = new LineString(t.getToken(p));
+ subgeoms[p] = new LineString(t.getToken(p), haveM);
}
dimension = subgeoms[0].dimension;
+ haveMeasure = subgeoms[0].haveMeasure;
} else {
throw new SQLException("postgis.multilinestringgeometry");
}
}
public MultiPoint(String value) throws SQLException {
- super(MULTIPOINT, value);
+ this(value, false);
+ }
+
+ protected MultiPoint(String value, boolean haveM) throws SQLException {
+ super(MULTIPOINT, value, haveM);
}
}
\ No newline at end of file
}
public MultiPolygon(String value) throws SQLException {
+ this(value, false);
+ }
+
+ protected MultiPolygon(String value, boolean haveM) throws SQLException {
this();
value = value.trim();
if (value.indexOf("MULTIPOLYGON") == 0) {
- PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value.substring(12).trim()), ',');
+ int pfxlen = typestring.length();
+ if (value.charAt(pfxlen) == 'M') {
+ pfxlen += 1;
+ haveM = true;
+ }
+ value = value.substring(pfxlen).trim();
+ PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value), ',');
int npolygons = t.getSize();
subgeoms = new Polygon[npolygons];
for (int p = 0; p < npolygons; p++) {
- subgeoms[p] = new Polygon(t.getToken(p));
+ subgeoms[p] = new Polygon(t.getToken(p), haveM);
}
dimension = subgeoms[0].dimension;
+ // fetch haveMeasure from subpoint because haveM does only work with
+ // 2d+M, not with 3d+M geometries
+ this.haveMeasure = subgeoms[0].haveMeasure;
} else {
throw new SQLException("postgis.multipolygongeometry");
}
}
public static Geometry geomFromString(String value) throws SQLException {
+ return geomFromString(value, false);
+ }
+
+ public static Geometry geomFromString(String value, boolean haveM) throws SQLException {
BinaryParser bp = new BinaryParser();
- return geomFromString(value, bp);
+ return geomFromString(value, bp, haveM);
}
/**
* Maybe we could add more error checking here?
*/
public static Geometry geomFromString(String value, BinaryParser bp) throws SQLException {
+ return geomFromString(value, bp, false);
+ }
+
+ public static Geometry geomFromString(String value, BinaryParser bp, boolean haveM)
+ throws SQLException {
value = value.trim();
int srid = -1;
// geometry which is to be parsed as an empty GeometryCollection.
result = new GeometryCollection();
} else if (value.startsWith("MULTIPOLYGON")) {
- result = new MultiPolygon(value);
+ result = new MultiPolygon(value, haveM);
} else if (value.startsWith("MULTILINESTRING")) {
- result = new MultiLineString(value);
+ result = new MultiLineString(value, haveM);
} else if (value.startsWith("MULTIPOINT")) {
- result = new MultiPoint(value);
+ result = new MultiPoint(value, haveM);
} else if (value.startsWith("POLYGON")) {
- result = new Polygon(value);
+ result = new Polygon(value, haveM);
} else if (value.startsWith("LINESTRING")) {
- result = new LineString(value);
+ result = new LineString(value, haveM);
} else if (value.startsWith("POINT")) {
- result = new Point(value);
+ result = new Point(value, haveM);
} else if (value.startsWith("GEOMETRYCOLLECTION")) {
- result = new GeometryCollection(value);
+ result = new GeometryCollection(value, haveM);
} else {
throw new SQLException("Unknown type: " + value);
}
protected boolean equalsintern(Geometry otherg) {
Point other = (Point) otherg;
- return x == other.x && y == other.y && ((dimension == 2) || (z == other.z))
- && ((haveMeasure == false) || (m == other.m));
+ boolean xequals = x == other.x;
+ boolean zequals = ((dimension == 2) || (z == other.z));
+ boolean mequals = ((haveMeasure == false) || (m == other.m));
+ boolean result = xequals && yequals(other) && zequals && mequals;
+ return result;
+ }
+
+ private boolean yequals(Point other) {
+ return y == other.y;
}
public Point getPoint(int index) {
dimension = 2;
}
+ /**
+ * Construct a Point from EWKT.
+ *
+ * (3D and measures are legal, but SRID is not allowed).
+ */
public Point(String value) throws SQLException {
+ this(value, false);
+ }
+
+ /**
+ * Construct a Point
+ *
+ * @param value The text representation of this point
+ * @param haveM Hint whether we have a measure. This is used by other
+ * geometries parsing inner points where we only get "1 2 3 4"
+ * like strings without the "POINT(" and ")" stuff. If there
+ * acutally is a POINTM prefix, this overrides the given value.
+ * However, POINT does not set it to false, as they can be
+ * contained in measured collections, as in
+ * "GEOMETRYCOLLECTIONM(POINT(0 0 0))".
+ */
+ protected Point(String value, boolean haveM) throws SQLException {
this();
value = value.trim();
- if (value.indexOf("POINT") == 0) {
+ if (value.indexOf("POINTM") == 0) {
+ haveM = true;
+ value = value.substring(6).trim();
+ } else if (value.indexOf("POINT") == 0) {
value = value.substring(5).trim();
}
PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value), ' ');
try {
- if (t.getSize() == 3) {
- x = Double.valueOf(t.getToken(0)).doubleValue();
- y = Double.valueOf(t.getToken(1)).doubleValue();
+ x = Double.valueOf(t.getToken(0)).doubleValue();
+ y = Double.valueOf(t.getToken(1)).doubleValue();
+ haveM |= t.getSize() == 4;
+ if ((t.getSize() == 3 && !haveM) || (t.getSize() == 4)) {
z = Double.valueOf(t.getToken(2)).doubleValue();
dimension = 3;
} else {
- x = Double.valueOf(t.getToken(0)).doubleValue();
- y = Double.valueOf(t.getToken(1)).doubleValue();
- z = 0.0;
dimension = 2;
}
+ if (haveM) {
+ m = Double.valueOf(t.getToken(dimension)).doubleValue();
+ }
} catch (NumberFormatException e) {
throw new SQLException("postgis.Point" + e.toString());
}
+ haveMeasure = haveM;
}
public void innerWKT(StringBuffer sb) {
}
public PointComposedGeom(int type, String value) throws SQLException {
+ this(type, value, false);
+ }
+ public PointComposedGeom(int type, String value, boolean haveM) throws SQLException {
this(type);
value = value.trim();
if (value.indexOf(typestring) == 0) {
- value = value.substring(typestring.length()).trim();
+ int pfxlen = typestring.length();
+ if (value.charAt(pfxlen)=='M') {
+ pfxlen += 1;
+ haveM=true;
+ }
+ value = value.substring(pfxlen).trim();
}
if (value.charAt(0) == '(') {
PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value), ',');
int npoints = t.getSize();
subgeoms = new Point[npoints];
for (int p = 0; p < npoints; p++) {
- subgeoms[p] = new Point(t.getToken(p));
+ subgeoms[p] = new Point(t.getToken(p), haveM);
}
dimension = subgeoms[0].dimension;
+ haveM |= subgeoms[0].haveMeasure;
} else {
throw new SQLException("postgis.multipointgeometry");
}
+ this.haveMeasure = haveM;
}
protected void innerWKT(StringBuffer sb) {
}
public Polygon(String value) throws SQLException {
+ this(value, false);
+ }
+
+ public Polygon(String value, boolean haveM) throws SQLException{
this();
value = value.trim();
- if (value.indexOf("POLYGON") == 0) {
- value = value.substring(7).trim();
+ if (value.indexOf(typestring) == 0) {
+ int pfxlen = typestring.length();
+ if (value.charAt(pfxlen) == 'M') {
+ pfxlen += 1;
+ haveM = true;
+ }
+ value = value.substring(pfxlen).trim();
}
PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(value), ',');
int nrings = t.getSize();
subgeoms = new LinearRing[nrings];
for (int r = 0; r < nrings; r++) {
- subgeoms[r] = new LinearRing(t.getToken(r));
+ subgeoms[r] = new LinearRing(t.getToken(r), haveM);
}
dimension = subgeoms[0].dimension;
+ // fetch haveMeasure from subpoint because haveM does only work with
+ // 2d+M, not with 3d+M geometries
+ this.haveMeasure = subgeoms[0].haveMeasure;
}
public int numRings() {
- Testing
-- WKT parsing of M and 4d geometries
-
- Handling of length() - esp. with modifying the geometries
- Handling of hashCode() - esp. with modifying the geometries