276 lines
6.2 KiB
Java
276 lines
6.2 KiB
Java
/*
|
|
* Copyright 2013 Hannes Janetzek
|
|
*
|
|
* This program 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 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.oscim.utils.wkb;
|
|
|
|
import org.oscim.core.GeometryBuffer;
|
|
import org.oscim.core.Tile;
|
|
|
|
public class WKBReader {
|
|
public interface Callback {
|
|
public void process(GeometryBuffer geom);
|
|
}
|
|
|
|
private final GeometryBuffer mGeom;
|
|
private final boolean mFlipY;
|
|
|
|
private WKBReader.Callback mCallback;
|
|
|
|
public WKBReader(GeometryBuffer geom, boolean flipY) {
|
|
mGeom = geom;
|
|
mFlipY = flipY;
|
|
}
|
|
|
|
public void setCallback(WKBReader.Callback cb) {
|
|
mCallback = cb;
|
|
}
|
|
|
|
/**
|
|
* Parse a binary encoded geometry.
|
|
*/
|
|
public void parse(byte[] value) {
|
|
parseGeometry(valueGetterForEndian(value), 0);
|
|
}
|
|
|
|
/**
|
|
* Parse a hex encoded geometry.
|
|
*/
|
|
public void parse(String value) {
|
|
byte[] b = hexStringToByteArray(value);
|
|
if (b == null)
|
|
return;
|
|
|
|
parse(b);
|
|
}
|
|
|
|
private void parseGeometry(ValueGetter data, int count) {
|
|
byte endian = data.getByte(); // skip and test endian flag
|
|
if (endian != data.endian) {
|
|
throw new IllegalArgumentException("Endian inconsistency!");
|
|
}
|
|
int typeword = data.getInt();
|
|
|
|
int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
|
|
|
|
boolean haveZ = (typeword & 0x80000000) != 0;
|
|
boolean haveM = (typeword & 0x40000000) != 0;
|
|
boolean haveS = (typeword & 0x20000000) != 0;
|
|
|
|
// int srid = Geometry.UNKNOWN_SRID;
|
|
if (haveS) {
|
|
// srid = Geometry.parseSRID(data.getInt());
|
|
data.getInt();
|
|
}
|
|
switch (realtype) {
|
|
case Geometry.POINT:
|
|
mGeom.startPoints();
|
|
parsePoint(data, haveZ, haveM);
|
|
break;
|
|
case Geometry.LINESTRING:
|
|
mGeom.startLine();
|
|
parseLineString(data, haveZ, haveM);
|
|
break;
|
|
case Geometry.POLYGON:
|
|
mGeom.startPolygon();
|
|
parsePolygon(data, haveZ, haveM);
|
|
break;
|
|
case Geometry.MULTIPOINT:
|
|
mGeom.startPoints();
|
|
parseMultiPoint(data);
|
|
break;
|
|
case Geometry.MULTILINESTRING:
|
|
mGeom.startLine();
|
|
parseMultiLineString(data);
|
|
break;
|
|
case Geometry.MULTIPOLYGON:
|
|
mGeom.startPolygon();
|
|
parseMultiPolygon(data);
|
|
break;
|
|
case Geometry.GEOMETRYCOLLECTION:
|
|
parseCollection(data);
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Unknown Geometry Type: " + realtype);
|
|
}
|
|
|
|
if (count == 0) {
|
|
mCallback.process(mGeom);
|
|
|
|
mGeom.clear();
|
|
}
|
|
// if (srid != Geometry.UNKNOWN_SRID) {
|
|
// result.setSrid(srid);
|
|
// }
|
|
}
|
|
|
|
private void parsePoint(ValueGetter data, boolean haveZ, boolean haveM) {
|
|
|
|
float x = (float) data.getDouble();
|
|
float y = (float) data.getDouble();
|
|
if (mFlipY)
|
|
y = Tile.SIZE - y;
|
|
|
|
mGeom.addPoint(x, y);
|
|
|
|
if (haveZ)
|
|
data.getDouble();
|
|
|
|
if (haveM)
|
|
data.getDouble();
|
|
}
|
|
|
|
/**
|
|
* Parse an Array of "full" Geometries
|
|
*
|
|
* @param data
|
|
* ...
|
|
* @param count
|
|
* ...
|
|
*/
|
|
private void parseGeometryArray(ValueGetter data, int count, int type) {
|
|
mGeom.clear();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
if (i > 0) {
|
|
if (type == Geometry.LINESTRING)
|
|
mGeom.startLine();
|
|
else if (type == Geometry.POLYGON)
|
|
mGeom.startPolygon();
|
|
else {
|
|
mCallback.process(mGeom);
|
|
mGeom.clear();
|
|
}
|
|
}
|
|
parseGeometry(data, count);
|
|
// mGeom.index[++mGeom.indexPos] = -1;
|
|
}
|
|
|
|
mCallback.process(mGeom);
|
|
mGeom.clear();
|
|
}
|
|
|
|
private void parseMultiPoint(ValueGetter data) {
|
|
parseGeometryArray(data, data.getInt(), Geometry.POINT);
|
|
}
|
|
|
|
private void parseLineString(ValueGetter data, boolean haveZ, boolean haveM) {
|
|
|
|
int count = data.getInt();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
float x = (float) data.getDouble();
|
|
float y = (float) data.getDouble();
|
|
|
|
if (mFlipY)
|
|
y = Tile.SIZE - y;
|
|
|
|
mGeom.addPoint(x, y);
|
|
|
|
// ignore
|
|
if (haveZ)
|
|
data.getDouble();
|
|
if (haveM)
|
|
data.getDouble();
|
|
}
|
|
}
|
|
|
|
private void parsePolygon(ValueGetter data, boolean haveZ, boolean haveM) {
|
|
int count = data.getInt();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
if (i > 0)
|
|
mGeom.startHole();
|
|
|
|
|
|
int points = data.getInt();
|
|
|
|
for (int j = 0; j < points; j++) {
|
|
float x = (float) data.getDouble();
|
|
float y = (float) data.getDouble();
|
|
|
|
if (mFlipY)
|
|
y = Tile.SIZE - y;
|
|
|
|
// drop redundant closing point
|
|
if (j < points - 1)
|
|
mGeom.addPoint(x, y);
|
|
|
|
// ignore
|
|
if (haveZ)
|
|
data.getDouble();
|
|
if (haveM)
|
|
data.getDouble();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void parseMultiLineString(ValueGetter data) {
|
|
|
|
int count = data.getInt();
|
|
if (count <= 0)
|
|
return;
|
|
|
|
parseGeometryArray(data, count, Geometry.LINESTRING);
|
|
}
|
|
|
|
private void parseMultiPolygon(ValueGetter data) {
|
|
int count = data.getInt();
|
|
if (count <= 0)
|
|
return;
|
|
|
|
parseGeometryArray(data, count, Geometry.POLYGON);
|
|
}
|
|
|
|
private void parseCollection(ValueGetter data) {
|
|
int count = data.getInt();
|
|
|
|
parseGeometryArray(data, count, Geometry.GEOMETRYCOLLECTION);
|
|
|
|
mCallback.process(mGeom);
|
|
mGeom.clear();
|
|
}
|
|
|
|
private static ValueGetter valueGetterForEndian(byte[] bytes) {
|
|
if (bytes[0] == ValueGetter.XDR.NUMBER) { // XDR
|
|
return new ValueGetter.XDR(bytes);
|
|
} else if (bytes[0] == ValueGetter.NDR.NUMBER) {
|
|
return new ValueGetter.NDR(bytes);
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown Endian type:" + bytes[0]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converting a string of hex character to bytes
|
|
*
|
|
* from http://stackoverflow.com/questions/140131/convert-a-string-
|
|
* representation-of-a-hex-dump-to-a-byte-array-using-java
|
|
*/
|
|
public static byte[] hexStringToByteArray(String s) {
|
|
|
|
int len = s.length();
|
|
if (len < 2)
|
|
return null;
|
|
|
|
byte[] data = new byte[len / 2];
|
|
for (int i = 0; i < len; i += 2) {
|
|
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
|
+ Character.digit(s.charAt(i + 1), 16));
|
|
}
|
|
return data;
|
|
}
|
|
|
|
}
|