added drawing api
add GeomBuilder, needs Jts drawing_api: squashed drawing_api: use JtsDrawable baseclass - add makeCircle for testing drawing_api: cleanups drawing_api: refactor drawing_api: use SpatialIndex drawing_api: optimization + cleanup drawing_api: VectorLayer - use VectorLayer for PathLayer drawing_api: make style builder more consistent with theme api drawing_api: wip
This commit is contained in:
parent
3402308ced
commit
b174f65122
@ -16,32 +16,52 @@
|
||||
*/
|
||||
package org.oscim.android.test;
|
||||
|
||||
import static org.oscim.tiling.source.bitmap.DefaultSources.STAMEN_TONER;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.layers.PathLayer;
|
||||
import org.oscim.map.Map.UpdateListener;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* This is a very INEFFICIENT and somewhat less usefull example for how to use
|
||||
* PathLayers!
|
||||
*/
|
||||
public class PathOverlayActivity extends BitmapTileMapActivity {
|
||||
|
||||
public PathOverlayActivity() {
|
||||
super(STAMEN_TONER.build());
|
||||
//super(STAMEN_TONER.build());
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mBitmapLayer.tileRenderer().setBitmapAlpha(0.5f);
|
||||
//mBitmapLayer.tileRenderer().setBitmapAlpha(0.5f);
|
||||
|
||||
createLayers(1, true);
|
||||
mMap.setMapPosition(0, 0, 1 << 2);
|
||||
for (double lat = -90; lat <= 90; lat += 5) {
|
||||
int c = Color.fade(Color.rainbow((float) (lat + 90) / 180), 0.5f);
|
||||
PathLayer pathLayer = new PathLayer(mMap, c, 6);
|
||||
mMap.layers().add(pathLayer);
|
||||
mPathLayers.add(pathLayer);
|
||||
}
|
||||
|
||||
looooop();
|
||||
mMap.events.bind(new UpdateListener() {
|
||||
@Override
|
||||
public void onMapEvent(Event e, MapPosition mapPosition) {
|
||||
//if (e == Map.UPDATE_EVENT) {
|
||||
long t = System.currentTimeMillis();
|
||||
float pos = t % 20000 / 10000f - 1f;
|
||||
createLayers(pos);
|
||||
|
||||
mMap.updateMap(true);
|
||||
//}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -52,40 +72,18 @@ public class PathOverlayActivity extends BitmapTileMapActivity {
|
||||
mMapView.onResume();
|
||||
}
|
||||
|
||||
void looooop() {
|
||||
mMap.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
long t = SystemClock.uptimeMillis();
|
||||
float pos = t % 20000 / 10000f - 1f;
|
||||
createLayers(pos, false);
|
||||
//Samples.log.debug("update took" + (SystemClock.uptimeMillis() - t) + " " + pos);
|
||||
looooop();
|
||||
redraw();
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
void redraw() {
|
||||
mMap.render();
|
||||
}
|
||||
|
||||
ArrayList<PathLayer> mPathLayers = new ArrayList<PathLayer>();
|
||||
|
||||
void createLayers(float pos, boolean init) {
|
||||
void createLayers(float pos) {
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (double lat = -90; lat <= 90; lat += 5) {
|
||||
List<GeoPoint> pts = new ArrayList<GeoPoint>();
|
||||
|
||||
double[] packedCoordinates = new double[360 + 2];
|
||||
//List<GeoPoint> pts = new ArrayList<GeoPoint>();
|
||||
int c = 0;
|
||||
for (double lon = -180; lon <= 180; lon += 2) {
|
||||
//pts.add(new GeoPoint(lat, lon));
|
||||
double longitude = lon + (pos * 180);
|
||||
if (longitude < -180)
|
||||
longitude += 360;
|
||||
if (longitude > 180)
|
||||
longitude -= 360;
|
||||
double longitude = lon;
|
||||
|
||||
double latitude = lat + (pos * 90);
|
||||
if (latitude < -90)
|
||||
@ -95,20 +93,15 @@ public class PathOverlayActivity extends BitmapTileMapActivity {
|
||||
|
||||
latitude += Math.sin((Math.abs(pos) * (lon / Math.PI)));
|
||||
|
||||
pts.add(new GeoPoint(latitude, longitude));
|
||||
}
|
||||
PathLayer pathLayer;
|
||||
if (init) {
|
||||
int c = Color.fade(Color.rainbow((float) (lat + 90) / 180), 0.5f);
|
||||
pathLayer = new PathLayer(mMap, c, 6);
|
||||
mMap.layers().add(pathLayer);
|
||||
mPathLayers.add(pathLayer);
|
||||
} else {
|
||||
pathLayer = mPathLayers.get(i++);
|
||||
packedCoordinates[c++] = longitude;
|
||||
packedCoordinates[c++] = latitude;
|
||||
}
|
||||
|
||||
pathLayer.setPoints(pts);
|
||||
//LineString line = new LineString(factory.create(packedCoordinates, 2), geomFactory);
|
||||
//mPathLayers.get(i++).setLineString(line);
|
||||
|
||||
mPathLayers.get(i++).setLineString(packedCoordinates);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.oscim.layers;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.osm.OsmElement;
|
||||
import org.oscim.layers.vector.AbstractVectorLayer;
|
||||
import org.oscim.map.Map;
|
||||
@ -12,7 +12,7 @@ public class OsmVectorLayer extends AbstractVectorLayer<OsmElement> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processFeatures(Task t, BoundingBox b) {
|
||||
protected void processFeatures(Task t, Box b) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ public class JeoVectorLayer extends JtsLayer {
|
||||
private final RuleList mRules;
|
||||
|
||||
protected double mDropPointDistance = 0.01;
|
||||
private double mMinX;
|
||||
private double mMinY;
|
||||
|
||||
public JeoVectorLayer(Map map, VectorDataset data, Style style) {
|
||||
super(map);
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.oscim.layers;
|
||||
|
||||
import org.jeo.geom.CoordinatePath;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
@ -19,14 +19,16 @@ import com.vividsolutions.jts.geom.Geometry;
|
||||
|
||||
public abstract class JtsLayer extends AbstractVectorLayer<Geometry> {
|
||||
|
||||
private double mMinX;
|
||||
private double mMinY;
|
||||
|
||||
public JtsLayer(Map map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processFeatures(Task t, BoundingBox bbox) {
|
||||
processFeatures(t, new Envelope(bbox.getMinLongitude(), bbox.getMaxLongitude(),
|
||||
bbox.getMinLatitude(), bbox.getMaxLatitude()));
|
||||
protected void processFeatures(Task t, Box bbox) {
|
||||
processFeatures(t, new Envelope(bbox.xmin, bbox.ymin, bbox.xmax, bbox.ymax));
|
||||
|
||||
}
|
||||
|
||||
|
83
vtm-playground/src/org/oscim/test/PathLayerTest.java
Normal file
83
vtm-playground/src/org/oscim/test/PathLayerTest.java
Normal file
@ -0,0 +1,83 @@
|
||||
package org.oscim.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.gdx.GdxMapApp;
|
||||
import org.oscim.layers.PathLayer;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.map.Map.UpdateListener;
|
||||
|
||||
public class PathLayerTest extends GdxMapApp {
|
||||
|
||||
@Override
|
||||
public void createLayers() {
|
||||
createLayers(1, true);
|
||||
|
||||
mMap.setMapPosition(0, 0, 1 << 2);
|
||||
|
||||
mMap.events.bind(new UpdateListener() {
|
||||
@Override
|
||||
public void onMapEvent(Event e, MapPosition mapPosition) {
|
||||
if (e == Map.UPDATE_EVENT) {
|
||||
long t = System.currentTimeMillis();
|
||||
float pos = t % 20000 / 10000f - 1f;
|
||||
createLayers(pos, false);
|
||||
mMap.updateMap(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ArrayList<PathLayer> mPathLayers = new ArrayList<PathLayer>();
|
||||
|
||||
void createLayers(float pos, boolean init) {
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (double lat = -90; lat <= 90; lat += 5) {
|
||||
List<GeoPoint> pts = new ArrayList<GeoPoint>();
|
||||
|
||||
for (double lon = -180; lon <= 180; lon += 2) {
|
||||
//pts.add(new GeoPoint(lat, lon));
|
||||
// double longitude = lon + (pos * 180);
|
||||
// if (longitude < -180)
|
||||
// longitude += 360;
|
||||
// if (longitude > 180)
|
||||
// longitude -= 360;
|
||||
double longitude = lon;
|
||||
|
||||
double latitude = lat + (pos * 90);
|
||||
if (latitude < -90)
|
||||
latitude += 180;
|
||||
if (latitude > 90)
|
||||
latitude -= 180;
|
||||
|
||||
latitude += Math.sin((Math.abs(pos) * (lon / Math.PI)));
|
||||
|
||||
pts.add(new GeoPoint(latitude, longitude));
|
||||
}
|
||||
PathLayer pathLayer;
|
||||
if (init) {
|
||||
int c = Color.fade(Color.rainbow((float) (lat + 90) / 180), 0.5f);
|
||||
pathLayer = new PathLayer(mMap, c, 6);
|
||||
mMap.layers().add(pathLayer);
|
||||
mPathLayers.add(pathLayer);
|
||||
} else {
|
||||
pathLayer = mPathLayers.get(i++);
|
||||
}
|
||||
|
||||
pathLayer.setPoints(pts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
GdxMapApp.init();
|
||||
GdxMapApp.run(new PathLayerTest(), null, 400);
|
||||
}
|
||||
}
|
81
vtm-playground/src/org/oscim/test/VectorLayerTest.java
Normal file
81
vtm-playground/src/org/oscim/test/VectorLayerTest.java
Normal file
@ -0,0 +1,81 @@
|
||||
package org.oscim.test;
|
||||
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.gdx.GdxMapApp;
|
||||
import org.oscim.layers.vector.VectorLayer;
|
||||
import org.oscim.layers.vector.geometries.PointDrawable;
|
||||
import org.oscim.layers.vector.geometries.Style;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.utils.ColorUtil;
|
||||
|
||||
public class VectorLayerTest extends GdxMapApp {
|
||||
|
||||
@Override
|
||||
public void createLayers() {
|
||||
Map map = getMap();
|
||||
|
||||
//VectorTileLayer tileLayer = map.setBaseMap(new OSciMap4TileSource());
|
||||
|
||||
VectorLayer vectorLayer = new VectorLayer(map);
|
||||
|
||||
// vectorLayer.add(new PointDrawable(0, 180, Style.builder()
|
||||
// .setBuffer(10)
|
||||
// .setFillColor(Color.RED)
|
||||
// .setFillAlpha(0.5)
|
||||
// .build()));
|
||||
//
|
||||
// Geometry g = new GeomBuilder()
|
||||
// .point(180, 0)
|
||||
// .point()
|
||||
// .buffer(6)
|
||||
// .get();
|
||||
//
|
||||
// vectorLayer.add(new PolygonDrawable(g, defaultStyle()));
|
||||
//
|
||||
|
||||
Style.Builder sb = Style.builder()
|
||||
.buffer(0.4)
|
||||
.fillColor(Color.RED)
|
||||
.fillAlpha(0.2);
|
||||
|
||||
Style style = sb.fillAlpha(0.2).build();
|
||||
|
||||
// int tileSize = 5;
|
||||
// for (int x = -180; x < 200; x += tileSize) {
|
||||
// for (int y = -90; y < 90; y += tileSize) {
|
||||
// // Style style = sb.setFillAlpha(FastMath.clamp(FastMath.length(x, y) / 180, 0.2, 1))
|
||||
// // .build();
|
||||
//
|
||||
// vectorLayer.add(new RectangleDrawable(FastMath.clamp(y, -85, 85), x,
|
||||
// FastMath.clamp(y + tileSize - 0.1, -85, 85),
|
||||
// x + tileSize - 0.1, style));
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
style = sb.buffer(Math.random() * 1)
|
||||
.fillColor(ColorUtil.setHue(Color.RED,
|
||||
Math.random()))
|
||||
.fillAlpha(0.5)
|
||||
.build();
|
||||
|
||||
vectorLayer.add(new PointDrawable(Math.random() * 180 - 90,
|
||||
Math.random() * 360 - 180,
|
||||
style));
|
||||
|
||||
}
|
||||
|
||||
map.layers().add(vectorLayer);
|
||||
|
||||
//map.layers().add(new LabelLayer(map, tileLayer));
|
||||
//map.setTheme(VtmThemes.DEFAULT);
|
||||
|
||||
map.setMapPosition(0, 0, 1 << 2);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
GdxMapApp.init();
|
||||
GdxMapApp.run(new VectorLayerTest(), null, 400);
|
||||
}
|
||||
}
|
@ -268,7 +268,7 @@ public class SearchBox {
|
||||
} catch (Exception e) {
|
||||
log.debug(wkt);
|
||||
}
|
||||
mOverlay.setGeom(g);
|
||||
//FIXME mOverlay.setGeom(g);
|
||||
|
||||
//log.debug("add polygon " + p.length());
|
||||
} else {
|
||||
|
@ -5,6 +5,7 @@ configurations { providedCompile }
|
||||
|
||||
dependencies {
|
||||
compile 'org.slf4j:slf4j-api:1.7.6'
|
||||
compile 'com.vividsolutions:jts:1.13'
|
||||
providedCompile 'com.squareup.okhttp:okhttp:1.5.2'
|
||||
providedCompile 'com.google.code.findbugs:annotations:2.0.1'
|
||||
}
|
||||
@ -29,7 +30,10 @@ eclipse.classpath {
|
||||
|
||||
file.whenMerged { classpath ->
|
||||
classpath.entries.findAll { entry ->
|
||||
entry.path.contains('annotations') }*.exported = false
|
||||
entry.path.contains('annotations') ||
|
||||
entry.path.contains('okhttp') ||
|
||||
entry.path.contains('okio')
|
||||
}*.exported = false
|
||||
}
|
||||
|
||||
//if you don't want some classpath entries 'exported' in Eclipse
|
||||
|
@ -44,9 +44,10 @@ public class MapElement extends GeometryBuffer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
public MapElement clear() {
|
||||
layer = 5;
|
||||
super.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,39 +21,30 @@ package org.oscim.layers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.backend.canvas.Paint.Cap;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.vector.VectorLayer;
|
||||
import org.oscim.layers.vector.geometries.LineDrawable;
|
||||
import org.oscim.layers.vector.geometries.Style;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.renderer.BucketRenderer;
|
||||
import org.oscim.renderer.GLViewport;
|
||||
import org.oscim.renderer.bucket.LineBucket;
|
||||
import org.oscim.renderer.bucket.RenderBuckets;
|
||||
import org.oscim.theme.styles.LineStyle;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.utils.async.SimpleWorker;
|
||||
import org.oscim.utils.geom.LineClipper;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
import com.vividsolutions.jts.geom.LineString;
|
||||
|
||||
/** This class draws a path line in given color. */
|
||||
public class PathLayer extends Layer {
|
||||
public class PathLayer extends VectorLayer {
|
||||
|
||||
/** Stores points, converted to the map projection. */
|
||||
protected final ArrayList<GeoPoint> mPoints;
|
||||
protected boolean mUpdatePoints;
|
||||
|
||||
/** Line style */
|
||||
LineStyle mLineStyle;
|
||||
|
||||
final Worker mWorker;
|
||||
protected Style mStyle;
|
||||
protected LineDrawable mDrawable;
|
||||
|
||||
public PathLayer(Map map, int lineColor, float lineWidth) {
|
||||
super(map);
|
||||
mWorker = new Worker(map);
|
||||
mLineStyle = new LineStyle(lineColor, lineWidth, Cap.BUTT);
|
||||
mRenderer = new RenderPath();
|
||||
mStyle = Style.builder()
|
||||
.strokeColor(lineColor)
|
||||
.strokeWidth(lineWidth)
|
||||
.build();
|
||||
|
||||
mPoints = new ArrayList<GeoPoint>();
|
||||
}
|
||||
|
||||
@ -61,60 +52,59 @@ public class PathLayer extends Layer {
|
||||
this(map, lineColor, 2);
|
||||
}
|
||||
|
||||
public void clearPath() {
|
||||
if (mPoints.isEmpty())
|
||||
return;
|
||||
public void setStyle(int lineColor, float lineWidth) {
|
||||
mStyle = Style.builder()
|
||||
.strokeColor(lineColor)
|
||||
.strokeWidth(lineWidth)
|
||||
.build();
|
||||
}
|
||||
|
||||
synchronized (mPoints) {
|
||||
public void clearPath() {
|
||||
if (!mPoints.isEmpty())
|
||||
mPoints.clear();
|
||||
}
|
||||
|
||||
updatePoints();
|
||||
}
|
||||
|
||||
public void setPoints(List<GeoPoint> pts) {
|
||||
synchronized (mPoints) {
|
||||
mPoints.clear();
|
||||
mPoints.addAll(pts);
|
||||
}
|
||||
mPoints.clear();
|
||||
mPoints.addAll(pts);
|
||||
updatePoints();
|
||||
}
|
||||
|
||||
public void addPoint(GeoPoint pt) {
|
||||
synchronized (mPoints) {
|
||||
mPoints.add(pt);
|
||||
}
|
||||
mPoints.add(pt);
|
||||
updatePoints();
|
||||
}
|
||||
|
||||
public void addPoint(int latitudeE6, int longitudeE6) {
|
||||
synchronized (mPoints) {
|
||||
mPoints.add(new GeoPoint(latitudeE6, longitudeE6));
|
||||
}
|
||||
mPoints.add(new GeoPoint(latitudeE6, longitudeE6));
|
||||
updatePoints();
|
||||
}
|
||||
|
||||
private void updatePoints() {
|
||||
mWorker.submit(10);
|
||||
mUpdatePoints = true;
|
||||
synchronized (this) {
|
||||
|
||||
if (mDrawable != null) {
|
||||
remove(mDrawable);
|
||||
mDrawable = null;
|
||||
}
|
||||
|
||||
if (!mPoints.isEmpty()) {
|
||||
mDrawable = new LineDrawable(mPoints, mStyle);
|
||||
if (mDrawable.getGeometry() == null)
|
||||
mDrawable = null;
|
||||
else
|
||||
add(mDrawable);
|
||||
}
|
||||
}
|
||||
mWorker.submit(0);
|
||||
}
|
||||
|
||||
public List<GeoPoint> getPoints() {
|
||||
return mPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME To be removed
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
*/
|
||||
public void setGeom(GeometryBuffer geom) {
|
||||
mGeom = geom;
|
||||
mWorker.submit(10);
|
||||
}
|
||||
|
||||
GeometryBuffer mGeom;
|
||||
|
||||
/**
|
||||
* Draw a great circle. Calculate a point for every 100km along the path.
|
||||
*
|
||||
@ -148,11 +138,14 @@ public class PathLayer extends Layer {
|
||||
*/
|
||||
public void addGreatCircle(GeoPoint startPoint, GeoPoint endPoint,
|
||||
final int numberOfPoints) {
|
||||
// adapted from page
|
||||
// http://compastic.blogspot.co.uk/2011/07/how-to-draw-great-circle-on-map-in.html
|
||||
// which was adapted from page http://maps.forum.nu/gm_flight_path.html
|
||||
/* adapted from page
|
||||
* http://compastic.blogspot.co.uk/2011/07/how-to-draw-great-circle-on-map
|
||||
* -in.html
|
||||
* which was adapted from page http://maps.forum.nu/gm_flight_path.html */
|
||||
|
||||
// convert to radians
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
|
||||
/* convert to radians */
|
||||
double lat1 = startPoint.getLatitude() * Math.PI / 180;
|
||||
double lon1 = startPoint.getLongitude() * Math.PI / 180;
|
||||
double lat2 = endPoint.getLatitude() * Math.PI / 180;
|
||||
@ -181,230 +174,31 @@ public class PathLayer extends Layer {
|
||||
|
||||
double latN = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
|
||||
double lonN = Math.atan2(y, x);
|
||||
addPoint((int) (latN / (Math.PI / 180) * 1E6), (int) (lonN / (Math.PI / 180) * 1E6));
|
||||
|
||||
gb.point(latN / (Math.PI / 180), lonN / (Math.PI / 180));
|
||||
}
|
||||
|
||||
setLineString(gb.toLineString());
|
||||
}
|
||||
|
||||
/***
|
||||
* everything below runs on GL- and Worker-Thread
|
||||
***/
|
||||
final class RenderPath extends BucketRenderer {
|
||||
|
||||
public RenderPath() {
|
||||
|
||||
buckets.addLineBucket(0, mLineStyle);
|
||||
}
|
||||
|
||||
private int mCurX = -1;
|
||||
private int mCurY = -1;
|
||||
private int mCurZ = -1;
|
||||
|
||||
@Override
|
||||
public synchronized void update(GLViewport v) {
|
||||
int tz = 1 << v.pos.zoomLevel;
|
||||
int tx = (int) (v.pos.x * tz);
|
||||
int ty = (int) (v.pos.y * tz);
|
||||
|
||||
// update layers when map moved by at least one tile
|
||||
if ((tx != mCurX || ty != mCurY || tz != mCurZ)) {
|
||||
mWorker.submit(100);
|
||||
mCurX = tx;
|
||||
mCurY = ty;
|
||||
mCurZ = tz;
|
||||
}
|
||||
|
||||
Task t = mWorker.poll();
|
||||
if (t == null)
|
||||
return;
|
||||
|
||||
// keep position to render relative to current state
|
||||
mMapPosition.copy(t.pos);
|
||||
|
||||
// compile new layers
|
||||
buckets.set(t.bucket.get());
|
||||
compile();
|
||||
public void setLineString(LineString path) {
|
||||
synchronized (this) {
|
||||
if (mDrawable != null)
|
||||
remove(mDrawable);
|
||||
mDrawable = new LineDrawable(path, mStyle);
|
||||
add(mDrawable);
|
||||
}
|
||||
mWorker.submit(0);
|
||||
}
|
||||
|
||||
final static class Task {
|
||||
RenderBuckets bucket = new RenderBuckets();
|
||||
MapPosition pos = new MapPosition();
|
||||
public void setLineString(double[] lonLat) {
|
||||
synchronized (this) {
|
||||
if (mDrawable != null)
|
||||
remove(mDrawable);
|
||||
mDrawable = new LineDrawable(lonLat, mStyle);
|
||||
add(mDrawable);
|
||||
}
|
||||
mWorker.submit(0);
|
||||
}
|
||||
|
||||
final class Worker extends SimpleWorker<Task> {
|
||||
|
||||
// limit coords
|
||||
private final int max = 2048;
|
||||
|
||||
public Worker(Map map) {
|
||||
super(map, 0, new Task(), new Task());
|
||||
mClipper = new LineClipper(-max, -max, max, max);
|
||||
mPPoints = new float[0];
|
||||
}
|
||||
|
||||
private static final int MIN_DIST = 3;
|
||||
|
||||
// pre-projected points
|
||||
private double[] mPreprojected = new double[2];
|
||||
|
||||
// projected points
|
||||
private float[] mPPoints;
|
||||
private final LineClipper mClipper;
|
||||
private int mNumPoints;
|
||||
|
||||
@Override
|
||||
public boolean doWork(Task task) {
|
||||
|
||||
int size = mNumPoints;
|
||||
|
||||
if (mUpdatePoints) {
|
||||
synchronized (mPoints) {
|
||||
mUpdatePoints = false;
|
||||
mNumPoints = size = mPoints.size();
|
||||
|
||||
ArrayList<GeoPoint> geopoints = mPoints;
|
||||
double[] points = mPreprojected;
|
||||
|
||||
if (size * 2 >= points.length) {
|
||||
points = mPreprojected = new double[size * 2];
|
||||
mPPoints = new float[size * 2];
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
MercatorProjection.project(geopoints.get(i), points, i);
|
||||
}
|
||||
|
||||
} else if (mGeom != null) {
|
||||
GeometryBuffer geom = mGeom;
|
||||
mGeom = null;
|
||||
size = geom.index[0];
|
||||
|
||||
double[] points = mPreprojected;
|
||||
|
||||
if (size > points.length) {
|
||||
points = mPreprojected = new double[size * 2];
|
||||
mPPoints = new float[size * 2];
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i += 2)
|
||||
MercatorProjection.project(geom.points[i + 1],
|
||||
geom.points[i], points,
|
||||
i >> 1);
|
||||
mNumPoints = size = size >> 1;
|
||||
|
||||
}
|
||||
if (size == 0) {
|
||||
if (task.bucket.get() != null) {
|
||||
task.bucket.clear();
|
||||
mMap.render();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderBuckets layers = task.bucket;
|
||||
|
||||
LineBucket ll = layers.getLineBucket(0);
|
||||
ll.line = mLineStyle;
|
||||
ll.scale = ll.line.width;
|
||||
|
||||
mMap.getMapPosition(task.pos);
|
||||
|
||||
int zoomlevel = task.pos.zoomLevel;
|
||||
task.pos.scale = 1 << zoomlevel;
|
||||
|
||||
double mx = task.pos.x;
|
||||
double my = task.pos.y;
|
||||
double scale = Tile.SIZE * task.pos.scale;
|
||||
|
||||
// flip around dateline
|
||||
int flip = 0;
|
||||
int maxx = Tile.SIZE << (zoomlevel - 1);
|
||||
|
||||
int x = (int) ((mPreprojected[0] - mx) * scale);
|
||||
int y = (int) ((mPreprojected[1] - my) * scale);
|
||||
|
||||
if (x > maxx) {
|
||||
x -= (maxx * 2);
|
||||
flip = -1;
|
||||
} else if (x < -maxx) {
|
||||
x += (maxx * 2);
|
||||
flip = 1;
|
||||
}
|
||||
|
||||
mClipper.clipStart(x, y);
|
||||
|
||||
float[] projected = mPPoints;
|
||||
int i = addPoint(projected, 0, x, y);
|
||||
|
||||
float prevX = x;
|
||||
float prevY = y;
|
||||
|
||||
float[] segment = null;
|
||||
|
||||
for (int j = 2; j < size * 2; j += 2) {
|
||||
x = (int) ((mPreprojected[j + 0] - mx) * scale);
|
||||
y = (int) ((mPreprojected[j + 1] - my) * scale);
|
||||
|
||||
int flipDirection = 0;
|
||||
if (x > maxx) {
|
||||
x -= maxx * 2;
|
||||
flipDirection = -1;
|
||||
} else if (x < -maxx) {
|
||||
x += maxx * 2;
|
||||
flipDirection = 1;
|
||||
}
|
||||
|
||||
if (flip != flipDirection) {
|
||||
flip = flipDirection;
|
||||
if (i > 2)
|
||||
ll.addLine(projected, i, false);
|
||||
|
||||
mClipper.clipStart(x, y);
|
||||
i = addPoint(projected, 0, x, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
int clip = mClipper.clipNext(x, y);
|
||||
if (clip < 1) {
|
||||
if (i > 2)
|
||||
ll.addLine(projected, i, false);
|
||||
|
||||
if (clip < 0) {
|
||||
/* add line segment */
|
||||
segment = mClipper.getLine(segment, 0);
|
||||
ll.addLine(segment, 4, false);
|
||||
prevX = mClipper.outX2;
|
||||
prevY = mClipper.outY2;
|
||||
}
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
float dx = x - prevX;
|
||||
float dy = y - prevY;
|
||||
if ((i == 0) || FastMath.absMaxCmp(dx, dy, MIN_DIST)) {
|
||||
projected[i++] = prevX = x;
|
||||
projected[i++] = prevY = y;
|
||||
}
|
||||
}
|
||||
if (i > 2)
|
||||
ll.addLine(projected, i, false);
|
||||
|
||||
// trigger redraw to let renderer fetch the result.
|
||||
mMap.render();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(Task task) {
|
||||
task.bucket.clear();
|
||||
}
|
||||
|
||||
private int addPoint(float[] points, int i, int x, int y) {
|
||||
points[i++] = x;
|
||||
points[i++] = y;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
package org.oscim.layers.vector;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.map.Map;
|
||||
@ -26,13 +25,10 @@ public abstract class AbstractVectorLayer<T> extends Layer implements UpdateList
|
||||
protected final TileClipper mClipper = new TileClipper(-1024, -1024, 1024, 1024);
|
||||
|
||||
protected final Worker mWorker;
|
||||
protected long mUpdateDelay = 100;
|
||||
protected long mUpdateDelay = 50;
|
||||
|
||||
protected boolean mUpdate = true;
|
||||
|
||||
protected double mMinX;
|
||||
protected double mMinY;
|
||||
|
||||
public AbstractVectorLayer(Map map) {
|
||||
super(map);
|
||||
mWorker = new Worker(mMap);
|
||||
@ -52,7 +48,7 @@ public abstract class AbstractVectorLayer<T> extends Layer implements UpdateList
|
||||
mUpdate = false;
|
||||
mWorker.submit(0);
|
||||
} else if (e == Map.POSITION_EVENT || e == Map.CLEAR_EVENT) {
|
||||
// throttle worker
|
||||
/* throttle worker */
|
||||
mWorker.submit(mUpdateDelay);
|
||||
}
|
||||
}
|
||||
@ -61,7 +57,7 @@ public abstract class AbstractVectorLayer<T> extends Layer implements UpdateList
|
||||
mWorker.submit(0);
|
||||
}
|
||||
|
||||
abstract protected void processFeatures(Task t, BoundingBox b);
|
||||
abstract protected void processFeatures(Task t, Box b);
|
||||
|
||||
protected static class Task {
|
||||
public final RenderBuckets buckets = new RenderBuckets();
|
||||
@ -84,28 +80,69 @@ public abstract class AbstractVectorLayer<T> extends Layer implements UpdateList
|
||||
/** running on worker thread */
|
||||
@Override
|
||||
public boolean doWork(Task t) {
|
||||
Viewport v = mMap.viewport();
|
||||
BoundingBox bbox;
|
||||
|
||||
Box bbox;
|
||||
float[] box = new float[8];
|
||||
|
||||
Viewport v = mMap.viewport().getSyncViewport();
|
||||
synchronized (v) {
|
||||
bbox = v.getBBox(null, 0);
|
||||
mMap.getMapPosition(t.position);
|
||||
v.getMapExtents(box, 0);
|
||||
v.getMapPosition(t.position);
|
||||
}
|
||||
|
||||
double scale = t.position.scale * Tile.SIZE;
|
||||
/* Hmm what is this for? */
|
||||
// double scale = t.position.scale * Tile.SIZE;
|
||||
// t.position.x = (long) (t.position.x * scale) / scale;
|
||||
// t.position.y = (long) (t.position.y * scale) / scale;
|
||||
|
||||
bbox.map2mercator();
|
||||
|
||||
// double xmin = bbox.xmin;
|
||||
// double xmax = bbox.xmax;
|
||||
// Box lbox = null;
|
||||
// Box rbox = null;
|
||||
// if (bbox.xmin < -180) {
|
||||
// bbox.xmin = -180;
|
||||
// lbox = new Box(bbox);
|
||||
// }
|
||||
// if (bbox.xmax > 180) {
|
||||
// bbox.xmax = 180;
|
||||
// rbox = new Box(bbox);
|
||||
// }
|
||||
|
||||
t.position.x = (long) (t.position.x * scale) / scale;
|
||||
t.position.y = (long) (t.position.y * scale) / scale;
|
||||
processFeatures(t, bbox);
|
||||
|
||||
//if (lbox != null) {
|
||||
// t.position.x += 1;
|
||||
// lbox.xmax = 180;
|
||||
// lbox.xmin = xmin + 180;
|
||||
// processFeatures(t, lbox);
|
||||
// t.position.x -= 1;
|
||||
//}
|
||||
//
|
||||
//if (rbox != null) {
|
||||
// t.position.x -= 1;
|
||||
// rbox.xmin = -180;
|
||||
// rbox.xmax = xmax - 180;
|
||||
// processFeatures(t, rbox);
|
||||
// t.position.x += 1;
|
||||
//}
|
||||
|
||||
t.buckets.prepare();
|
||||
|
||||
mMap.render();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Renderer extends BucketRenderer {
|
||||
MapPosition mTmpPos = new MapPosition();
|
||||
|
||||
public Renderer() {
|
||||
mFlipOnDateLine = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GLViewport v) {
|
||||
|
||||
@ -120,7 +157,6 @@ public abstract class AbstractVectorLayer<T> extends Layer implements UpdateList
|
||||
buckets.setFrom(t.buckets);
|
||||
|
||||
compile();
|
||||
//log.debug("is ready " + isReady() + " " + layers.getSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
81
vtm/src/org/oscim/layers/vector/JtsConverter.java
Normal file
81
vtm/src/org/oscim/layers/vector/JtsConverter.java
Normal file
@ -0,0 +1,81 @@
|
||||
package org.oscim.layers.vector;
|
||||
|
||||
import static org.oscim.core.MercatorProjection.latitudeToY;
|
||||
import static org.oscim.core.MercatorProjection.longitudeToX;
|
||||
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
import com.vividsolutions.jts.geom.CoordinateSequence;
|
||||
import com.vividsolutions.jts.geom.LineString;
|
||||
import com.vividsolutions.jts.geom.Point;
|
||||
import com.vividsolutions.jts.geom.Polygon;
|
||||
|
||||
public class JtsConverter {
|
||||
double x, y, scale;
|
||||
final double outScale;
|
||||
|
||||
public void setPosition(double x, double y, double scale) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.scale = scale * outScale;
|
||||
}
|
||||
|
||||
public JtsConverter(double outScale) {
|
||||
this.outScale = outScale;
|
||||
}
|
||||
|
||||
private final Coordinate mTmpCoord = new Coordinate();
|
||||
|
||||
public void transformPolygon(GeometryBuffer g, Polygon polygon) {
|
||||
Coordinate coord = mTmpCoord;
|
||||
|
||||
CoordinateSequence ring = polygon.getExteriorRing().getCoordinateSequence();
|
||||
|
||||
g.startPolygon();
|
||||
for (int j = 0; j < ring.size() - 1; j++) {
|
||||
ring.getCoordinate(j, coord);
|
||||
addPoint(g, coord);
|
||||
}
|
||||
for (int j = 0, n = polygon.getNumInteriorRing(); j < n; j++) {
|
||||
g.startHole();
|
||||
ring = polygon.getInteriorRingN(j).getCoordinateSequence();
|
||||
for (int k = 0; k < ring.size() - 1; k++) {
|
||||
ring.getCoordinate(k, coord);
|
||||
addPoint(g, coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void transformLineString(GeometryBuffer g, LineString linestring) {
|
||||
Coordinate coord = mTmpCoord;
|
||||
|
||||
CoordinateSequence line = linestring.getCoordinateSequence();
|
||||
|
||||
g.startLine();
|
||||
for (int j = 0, n = line.size(); j < n; j++) {
|
||||
line.getCoordinate(j, coord);
|
||||
addPoint(g, coord);
|
||||
}
|
||||
}
|
||||
|
||||
public void transformPoint(GeometryBuffer g, Point point) {
|
||||
Coordinate coord = mTmpCoord;
|
||||
|
||||
g.startPoints();
|
||||
coord.x = point.getX();
|
||||
coord.y = point.getY();
|
||||
addPoint(g, coord);
|
||||
}
|
||||
|
||||
public void addPoint(GeometryBuffer g, Coordinate coord) {
|
||||
g.addPoint((float) ((longitudeToX(coord.x) - x) * scale),
|
||||
(float) ((latitudeToY(coord.y) - y) * scale));
|
||||
}
|
||||
|
||||
public void addPoint(GeometryBuffer g, double lon, double lat) {
|
||||
g.addPoint((float) ((longitudeToX(lon) - x) * scale),
|
||||
(float) ((latitudeToY(lat) - y) * scale));
|
||||
}
|
||||
|
||||
}
|
321
vtm/src/org/oscim/layers/vector/VectorLayer.java
Normal file
321
vtm/src/org/oscim/layers/vector/VectorLayer.java
Normal file
@ -0,0 +1,321 @@
|
||||
package org.oscim.layers.vector;
|
||||
|
||||
import static org.oscim.core.MercatorProjection.latitudeToY;
|
||||
import static org.oscim.core.MercatorProjection.longitudeToX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.GeometryBuffer;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.vector.geometries.Drawable;
|
||||
import org.oscim.layers.vector.geometries.LineDrawable;
|
||||
import org.oscim.layers.vector.geometries.PointDrawable;
|
||||
import org.oscim.layers.vector.geometries.Style;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.renderer.bucket.LineBucket;
|
||||
import org.oscim.renderer.bucket.MeshBucket;
|
||||
import org.oscim.theme.styles.AreaStyle;
|
||||
import org.oscim.theme.styles.LineStyle;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.utils.QuadTree;
|
||||
import org.oscim.utils.SpatialIndex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.vividsolutions.jts.geom.Envelope;
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.LineString;
|
||||
import com.vividsolutions.jts.geom.Point;
|
||||
import com.vividsolutions.jts.geom.Polygon;
|
||||
import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier;
|
||||
|
||||
/* TODO keep bounding box of geometries - only try to render when bbox intersects viewport */
|
||||
|
||||
/**
|
||||
* Use this layer to draw predefined geometries from layers.vector.geometries
|
||||
* package and
|
||||
* JTS geometries together with a GeometryStyle
|
||||
*
|
||||
*/
|
||||
public class VectorLayer extends AbstractVectorLayer<Drawable> {
|
||||
|
||||
public static final Logger log = LoggerFactory.getLogger(VectorLayer.class);
|
||||
|
||||
//private final SpatialIndex<Drawable> mDrawables = new RTree<Drawable>();
|
||||
protected final SpatialIndex<Drawable> mDrawables = new QuadTree<Drawable>(1 << 30, 18);
|
||||
|
||||
protected final List<Drawable> tmpDrawables = new ArrayList<Drawable>(128);
|
||||
|
||||
protected final JtsConverter mConverter;
|
||||
protected double mMinX;
|
||||
protected double mMinY;
|
||||
|
||||
private static class GeometryWithStyle implements Drawable {
|
||||
final Geometry geometry;
|
||||
final Style style;
|
||||
|
||||
GeometryWithStyle(Geometry g, Style s) {
|
||||
geometry = g;
|
||||
style = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Style getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Geometry getGeometry() {
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
|
||||
protected Polygon mEnvelope;
|
||||
|
||||
public VectorLayer(Map map, SpatialIndex<Drawable> index) {
|
||||
this(map);
|
||||
}
|
||||
|
||||
public VectorLayer(Map map) {
|
||||
super(map);
|
||||
mConverter = new JtsConverter(Tile.SIZE / UNSCALE_COORD);
|
||||
}
|
||||
|
||||
private static Box bbox(Geometry geometry, Style style) {
|
||||
Envelope e = geometry.getEnvelopeInternal();
|
||||
Box bbox = new Box(e.getMinX(), e.getMinY(), e.getMaxX(), e.getMaxY());
|
||||
//if ("Point".equals(geometry.getGeometryType())){
|
||||
// bbox.
|
||||
//}
|
||||
|
||||
bbox.scale(1E6);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a drawable to a list of geometries that have to be drawn in the next
|
||||
* map update.
|
||||
*
|
||||
* @param drawable
|
||||
*/
|
||||
public void add(Drawable drawable) {
|
||||
mDrawables.insert(bbox(drawable.getGeometry(), drawable.getStyle()), drawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a JTS geometry and a style to a list of geometries that have to be
|
||||
* drawn in the next map update.
|
||||
*
|
||||
* @param geometry
|
||||
* @param style
|
||||
*/
|
||||
public synchronized void add(Geometry geometry, Style style) {
|
||||
mDrawables.insert(bbox(geometry, style), new GeometryWithStyle(geometry, style));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the drawable from the list of drawn geometries.
|
||||
*
|
||||
* @param drawable
|
||||
*/
|
||||
public synchronized void remove(Drawable drawable) {
|
||||
mDrawables.remove(bbox(drawable.getGeometry(), drawable.getStyle()), drawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the JTS geometry and its style from the list of drawn geometries.
|
||||
*
|
||||
* @param geometry
|
||||
*/
|
||||
public synchronized void remove(Geometry geometry) {
|
||||
Drawable toRemove = null;
|
||||
Box bbox = bbox(geometry, null);
|
||||
|
||||
synchronized (this) {
|
||||
tmpDrawables.clear();
|
||||
mDrawables.search(bbox, tmpDrawables);
|
||||
for (Drawable d : tmpDrawables) {
|
||||
if (d.getGeometry() == geometry)
|
||||
toRemove = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove == null) {
|
||||
log.error("Can't find geometry to remove.");
|
||||
return;
|
||||
}
|
||||
|
||||
mDrawables.remove(bbox, toRemove);
|
||||
//mMap.render();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processFeatures(Task t, Box bbox) {
|
||||
//log.debug("bbox {}", bbox);
|
||||
if (Double.isNaN(bbox.xmin))
|
||||
return;
|
||||
|
||||
// mEnvelope = new GeomBuilder()
|
||||
// .point(bbox.xmin, bbox.ymin)
|
||||
// .point(bbox.xmin, bbox.ymax)
|
||||
// .point(bbox.xmax, bbox.ymax)
|
||||
// .point(bbox.xmax, bbox.ymin)
|
||||
// .point(bbox.xmin, bbox.ymin)
|
||||
// .toPolygon();
|
||||
|
||||
/* reduce lines points min distance */
|
||||
mMinX = ((bbox.xmax - bbox.xmin) / mMap.getWidth());
|
||||
mMinY = ((bbox.ymax - bbox.ymin) / mMap.getHeight());
|
||||
|
||||
mConverter.setPosition(t.position.x, t.position.y, t.position.scale);
|
||||
|
||||
bbox.scale(1E6);
|
||||
|
||||
int level = 0;
|
||||
Style lastStyle = null;
|
||||
|
||||
/* go through features, find the matching style and draw */
|
||||
synchronized (this) {
|
||||
tmpDrawables.clear();
|
||||
mDrawables.search(bbox, tmpDrawables);
|
||||
// TODO sort by some order...
|
||||
|
||||
for (Drawable d : tmpDrawables) {
|
||||
Style style = d.getStyle();
|
||||
draw(t, level, d, style);
|
||||
|
||||
if (style != lastStyle)
|
||||
level += 2;
|
||||
|
||||
lastStyle = style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void draw(Task task, int level, Drawable d, Style style) {
|
||||
Geometry geom = d.getGeometry();
|
||||
|
||||
if (d instanceof LineDrawable) {
|
||||
drawLine(task, level, geom, style);
|
||||
} else if (d instanceof PointDrawable) {
|
||||
drawPoint(task, level, geom, style);
|
||||
} else {
|
||||
drawPolygon(task, level, geom, style);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawPoint(Task t, int level, Geometry points, Style style) {
|
||||
|
||||
MeshBucket mesh = t.buckets.getMeshBucket(level);
|
||||
if (mesh.area == null) {
|
||||
mesh.area = new AreaStyle(Color.fade(style.fillColor,
|
||||
style.fillAlpha));
|
||||
}
|
||||
|
||||
LineBucket ll = t.buckets.getLineBucket(level + 1);
|
||||
if (ll.line == null) {
|
||||
ll.line = new LineStyle(2, style.strokeColor, style.strokeWidth);
|
||||
}
|
||||
|
||||
for (int i = 0; i < points.getNumGeometries(); i++) {
|
||||
Point p = (Point) points.getGeometryN(i);
|
||||
addCircle(mGeom.clear(), t.position, p.getX(), p.getY(), style);
|
||||
|
||||
if (!mClipper.clip(mGeom))
|
||||
continue;
|
||||
|
||||
mesh.addConvexMesh(mGeom);
|
||||
ll.addLine(mGeom);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawLine(Task t, int level, Geometry line, Style style) {
|
||||
|
||||
LineBucket ll = t.buckets.getLineBucket(level);
|
||||
if (ll.line == null) {
|
||||
ll.line = new LineStyle(0, style.strokeColor, style.strokeWidth);
|
||||
}
|
||||
|
||||
if (style.generalization != Style.GENERALIZATION_NONE) {
|
||||
line = DouglasPeuckerSimplifier.simplify(line, mMinX * style.generalization);
|
||||
}
|
||||
|
||||
//line = line.intersection(mEnvelope);
|
||||
|
||||
for (int i = 0; i < line.getNumGeometries(); i++) {
|
||||
mConverter.transformLineString(mGeom.clear(), (LineString) line.getGeometryN(i));
|
||||
if (!mClipper.clip(mGeom))
|
||||
continue;
|
||||
|
||||
ll.addLine(mGeom);
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawPolygon(Task t, int level, Geometry polygon, Style style) {
|
||||
|
||||
MeshBucket mesh = t.buckets.getMeshBucket(level);
|
||||
if (mesh.area == null) {
|
||||
mesh.area = new AreaStyle(Color.fade(style.fillColor,
|
||||
style.fillAlpha));
|
||||
}
|
||||
|
||||
LineBucket ll = t.buckets.getLineBucket(level + 1);
|
||||
if (ll.line == null) {
|
||||
ll.line = new LineStyle(2, style.strokeColor, style.strokeWidth);
|
||||
}
|
||||
|
||||
if (style.generalization != Style.GENERALIZATION_NONE) {
|
||||
polygon = DouglasPeuckerSimplifier.simplify(polygon, mMinX * style.generalization);
|
||||
}
|
||||
|
||||
// if (polygon.isRectangle())
|
||||
|
||||
for (int i = 0; i < polygon.getNumGeometries(); i++) {
|
||||
mConverter.transformPolygon(mGeom.clear(), (Polygon) polygon.getGeometryN(i));
|
||||
|
||||
if (mGeom.getNumPoints() < 3)
|
||||
continue;
|
||||
|
||||
if (!mClipper.clip(mGeom))
|
||||
continue;
|
||||
|
||||
mesh.addMesh(mGeom);
|
||||
ll.addLine(mGeom);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addCircle(GeometryBuffer g, MapPosition pos,
|
||||
double px, double py, Style style) {
|
||||
|
||||
double scale = pos.scale * Tile.SIZE / UNSCALE_COORD;
|
||||
double x = (longitudeToX(px) - pos.x) * scale;
|
||||
double y = (latitudeToY(py) - pos.y) * scale;
|
||||
|
||||
/* TODO in the next line I was only able to interpolate a function
|
||||
* that makes up for the zoom level. The circle should not grow, it
|
||||
* should stickto the map. 0.01 / (1 << startLvl) makes it retain
|
||||
* its size. Correction? */
|
||||
int zoomScale = (1 << style.scalingZoomLevel);
|
||||
|
||||
/* Keep the circle's size constant in relation to the underlying map */
|
||||
double radius = style.buffer;
|
||||
|
||||
if (pos.scale > zoomScale)
|
||||
radius = (radius * 0.01) / zoomScale * (scale - zoomScale);
|
||||
|
||||
int quality = (int) (Math.sqrt(radius) * 8);
|
||||
quality = FastMath.clamp(quality, 4, 32);
|
||||
|
||||
double step = 2.0 * Math.PI / quality;
|
||||
|
||||
g.startPolygon();
|
||||
for (int i = 0; i < quality; i++) {
|
||||
g.addPoint((float) (x + radius * Math.cos(i * step)),
|
||||
(float) (y + radius * Math.sin(i * step)));
|
||||
}
|
||||
}
|
||||
}
|
149
vtm/src/org/oscim/layers/vector/geometries/CircleDrawable.java
Normal file
149
vtm/src/org/oscim/layers/vector/geometries/CircleDrawable.java
Normal file
@ -0,0 +1,149 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
/**
|
||||
* Predefined class for drawing circles on the map. Circles are by default
|
||||
* made of 32 segments.
|
||||
*/
|
||||
public class CircleDrawable extends JtsDrawable {
|
||||
|
||||
public static int MEDIUM_QUALITY = 32;
|
||||
public static int HIGH_QUALITY = 64;
|
||||
|
||||
/**
|
||||
* Constructs a circle given the real-world radius in km. Keep in mind that
|
||||
* this technique is computationally costly for circles with huge number or
|
||||
* segments.
|
||||
*
|
||||
* @param center GeoPoint - center of the circle
|
||||
* @param radiusKm Radius of the circle in kilometers.
|
||||
*/
|
||||
public CircleDrawable(GeoPoint center, double radiusKm) {
|
||||
super(Style.DEFAULT_STYLE);
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
for (int i = 0; i < MEDIUM_QUALITY; i++) {
|
||||
GeoPoint point = findGeoPointWithGivenDistance(center,
|
||||
i * Math.PI / MEDIUM_QUALITY * 2,
|
||||
radiusKm);
|
||||
gb.points(point.getLongitude(), point.getLatitude());
|
||||
}
|
||||
geometry = gb.toPolygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a circle given the real-world radius in km. Keep in mind that
|
||||
* this technique is computationally costly for circles with huge number or
|
||||
* segments.
|
||||
*
|
||||
* @param center GeoPoint - center of the circle
|
||||
* @param radiusKm Radius of the circle in kilometers. The size of the
|
||||
* circle may be distorted due to the Mercator projections
|
||||
* properties.
|
||||
* @param style FillableGeometryStyle with color and transparency
|
||||
* information for the circle
|
||||
*/
|
||||
public CircleDrawable(GeoPoint center, double radiusKm, Style style) {
|
||||
super(style);
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
for (int i = 0; i < MEDIUM_QUALITY; i++) {
|
||||
GeoPoint point = findGeoPointWithGivenDistance(center,
|
||||
i * Math.PI / MEDIUM_QUALITY * 2,
|
||||
radiusKm);
|
||||
gb.points(point.getLongitude(), point.getLatitude());
|
||||
}
|
||||
geometry = gb.toPolygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a circle given the real-world radius in km. Keep in mind that
|
||||
* this technique is computationally costly for circles with huge number or
|
||||
* segments.
|
||||
*
|
||||
* @param center GeoPoint - center of the circle
|
||||
* @param radiusKm Radius of the circle in kilometers. The size of the
|
||||
* circle may be distorted due to the Mercator projections
|
||||
* properties.
|
||||
* @param quadrantSegments the number of segments a quarter of circle will
|
||||
* have. Use Circle.LOW_PRECISION for quick rendering,
|
||||
* Circle.MEDIUM_PRECISION for good rendering and quality or
|
||||
* Circle.HIGH_PRECISION for high quality.
|
||||
* @param style FillableGeometryStyle with color and transparency
|
||||
* information for the circle
|
||||
*/
|
||||
public CircleDrawable(GeoPoint center, double radiusKm, int quadrantSegments,
|
||||
Style style) {
|
||||
super(style);
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
for (int i = 0; i < quadrantSegments; i++) {
|
||||
GeoPoint point = findGeoPointWithGivenDistance(center,
|
||||
i * Math.PI / quadrantSegments * 2,
|
||||
radiusKm);
|
||||
gb.points(point.getLongitude(), point.getLatitude());
|
||||
}
|
||||
geometry = gb.toPolygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function finds a GeoPoint offset by a distance in the direction
|
||||
* given in the bearing parameter. It is an approximation due to the
|
||||
* Mercator projections properties
|
||||
*
|
||||
* @param startPoint
|
||||
* @param initialBearingRadians
|
||||
* @param distanceKilometres
|
||||
* @return a new GeoPoint located distanceKilometers away from the
|
||||
* startPoint in the direction of the initialBearing
|
||||
*/
|
||||
private static GeoPoint findGeoPointWithGivenDistance(GeoPoint startPoint,
|
||||
double initialBearingRadians, double distanceKilometres)
|
||||
{
|
||||
double radiusEarthKilometres = 6371.01;
|
||||
double distRatio = distanceKilometres / radiusEarthKilometres;
|
||||
double distRatioSine = Math.sin(distRatio);
|
||||
double distRatioCosine = Math.cos(distRatio);
|
||||
|
||||
double startLatRad = degreesToRadians(startPoint.getLatitude());
|
||||
double startLonRad = degreesToRadians(startPoint.getLongitude());
|
||||
|
||||
double startLatCos = Math.cos(startLatRad);
|
||||
double startLatSin = Math.sin(startLatRad);
|
||||
|
||||
double endLatRads = Math.asin((startLatSin * distRatioCosine)
|
||||
+ (startLatCos * distRatioSine * Math.cos(initialBearingRadians)));
|
||||
|
||||
double endLonRads = startLonRad
|
||||
+ Math.atan2(
|
||||
Math.sin(initialBearingRadians) * distRatioSine * startLatCos,
|
||||
distRatioCosine - startLatSin * Math.sin(endLatRads));
|
||||
|
||||
return new GeoPoint(radiansToDegrees(endLatRads), radiansToDegrees(endLonRads));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* translates an angle from degrees to radians
|
||||
*
|
||||
* @param degrees
|
||||
* @return the angle in radians
|
||||
*/
|
||||
private static double degreesToRadians(double degrees)
|
||||
{
|
||||
double degToRadFactor = Math.PI / 180;
|
||||
return degrees * degToRadFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* translates an angle from radians to degrees
|
||||
*
|
||||
* @param radians
|
||||
* @return the angle in degrees
|
||||
*/
|
||||
private static double radiansToDegrees(double radians)
|
||||
{
|
||||
double radToDegFactor = 180 / Math.PI;
|
||||
return radians * radToDegFactor;
|
||||
}
|
||||
|
||||
}
|
16
vtm/src/org/oscim/layers/vector/geometries/Drawable.java
Normal file
16
vtm/src/org/oscim/layers/vector/geometries/Drawable.java
Normal file
@ -0,0 +1,16 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
|
||||
public interface Drawable {
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Style getStyle();
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Geometry getGeometry();
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Point;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
/**
|
||||
* Predefined class for drawing hexagons on the map.
|
||||
*/
|
||||
public class HexagonDrawable extends JtsDrawable {
|
||||
|
||||
/**
|
||||
* @param center GeoPoint - center of the hexagon
|
||||
* @param radiusKm Radius of the hexagon in kilometers. The size of the
|
||||
* hexagon may be distorted due to the Mercator projections
|
||||
* properties.
|
||||
*/
|
||||
public HexagonDrawable(GeoPoint center, double radiusKm) {
|
||||
super(Style.DEFAULT_STYLE);
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
GeoPoint point = findGeoPointWithGivenDistance(center, i * Math.PI / 3, radiusKm);
|
||||
gb.points(point.getLongitude(), point.getLatitude());
|
||||
}
|
||||
geometry = gb.toPolygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param center GeoPoint - center of the hexagon
|
||||
* @param radiusKm Radius of the hexagon in kilometers. The size of the
|
||||
* hexagon may be distorted due to the Mercator projections
|
||||
* properties.
|
||||
* @param rotationRad rotation of the hexagon in radians
|
||||
* @param style
|
||||
*/
|
||||
public HexagonDrawable(GeoPoint center, double radiusKm, double rotationRad, Style style) {
|
||||
super(style);
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
Point tmp = new Point();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
GeoPoint point = findGeoPointWithGivenDistance(center,
|
||||
rotationRad + i * Math.PI / 3,
|
||||
radiusKm);
|
||||
MercatorProjection.project(point, tmp);
|
||||
gb.points(tmp.x, tmp.y);
|
||||
}
|
||||
geometry = gb.toPolygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function finds a GeoPoint offset by a distance in the direction
|
||||
* given in the bearing parameter. It is an approximation due to the
|
||||
* Mercator projections properties
|
||||
*
|
||||
* @param startPoint
|
||||
* @param initialBearingRadians
|
||||
* @param distanceKilometres
|
||||
* @return a new GeoPoint located distanceKilometers away from the
|
||||
* startPoint in the direction of the initialBearing
|
||||
*/
|
||||
private static GeoPoint findGeoPointWithGivenDistance(GeoPoint startPoint,
|
||||
double initialBearingRadians, double distanceKilometres)
|
||||
{
|
||||
double radiusEarthKilometres = 6371.01;
|
||||
double distRatio = distanceKilometres / radiusEarthKilometres;
|
||||
double distRatioSine = Math.sin(distRatio);
|
||||
double distRatioCosine = Math.cos(distRatio);
|
||||
|
||||
double startLatRad = Math.toRadians(startPoint.getLatitude());
|
||||
double startLonRad = Math.toRadians(startPoint.getLongitude());
|
||||
|
||||
double startLatCos = Math.cos(startLatRad);
|
||||
double startLatSin = Math.sin(startLatRad);
|
||||
|
||||
double endLatRads = Math.asin((startLatSin * distRatioCosine)
|
||||
+ (startLatCos * distRatioSine * Math.cos(initialBearingRadians)));
|
||||
|
||||
double endLonRads = startLonRad
|
||||
+ Math.atan2(
|
||||
Math.sin(initialBearingRadians) * distRatioSine * startLatCos,
|
||||
distRatioCosine - startLatSin * Math.sin(endLatRads));
|
||||
|
||||
return new GeoPoint(Math.toDegrees(endLatRads), Math.toDegrees(endLonRads));
|
||||
}
|
||||
}
|
64
vtm/src/org/oscim/layers/vector/geometries/JtsDrawable.java
Normal file
64
vtm/src/org/oscim/layers/vector/geometries/JtsDrawable.java
Normal file
@ -0,0 +1,64 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.GeometryFactory;
|
||||
import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
|
||||
|
||||
public class JtsDrawable implements Drawable {
|
||||
|
||||
public static final PackedCoordinateSequenceFactory coordFactory;
|
||||
public static final GeometryFactory geomFactory;
|
||||
static {
|
||||
coordFactory = new PackedCoordinateSequenceFactory();
|
||||
geomFactory = new GeometryFactory(coordFactory);
|
||||
}
|
||||
|
||||
protected Style style;
|
||||
protected Geometry geometry;
|
||||
|
||||
public JtsDrawable(Style style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public JtsDrawable(Geometry geometry, Style style) {
|
||||
this.geometry = geometry;
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param style
|
||||
*/
|
||||
public void setStyle(Style style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
*
|
||||
* @see org.oscim.core.geometries.Drawable#getStyle() */
|
||||
@Override
|
||||
public Style getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
*
|
||||
* @see org.oscim.core.geometries.Drawable#getGeometry() */
|
||||
@Override
|
||||
public Geometry getGeometry() {
|
||||
return geometry;
|
||||
}
|
||||
|
||||
protected static GeomBuilder loadPoints(GeomBuilder gb, List<GeoPoint> points) {
|
||||
for (GeoPoint point : points) {
|
||||
gb.point(point.getLongitude(),
|
||||
point.getLatitude());
|
||||
}
|
||||
return gb;
|
||||
}
|
||||
|
||||
}
|
44
vtm/src/org/oscim/layers/vector/geometries/LineDrawable.java
Normal file
44
vtm/src/org/oscim/layers/vector/geometries/LineDrawable.java
Normal file
@ -0,0 +1,44 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.LineString;
|
||||
|
||||
/**
|
||||
* Predefined class for drawing lines and line strings on the map.
|
||||
*/
|
||||
public class LineDrawable extends JtsDrawable {
|
||||
|
||||
public LineDrawable(Geometry line, Style style) {
|
||||
super(style);
|
||||
if (line.getDimension() != 1)
|
||||
throw new IllegalArgumentException("Geometry not a Line");
|
||||
|
||||
this.geometry = line;
|
||||
}
|
||||
|
||||
public LineDrawable(List<GeoPoint> points) {
|
||||
this(points, Style.defaultStyle());
|
||||
}
|
||||
|
||||
public LineDrawable(List<GeoPoint> points, Style style) {
|
||||
super(style);
|
||||
if (points.size() < 2)
|
||||
return;
|
||||
|
||||
double[] coords = new double[points.size() * 2];
|
||||
int c = 0;
|
||||
for (GeoPoint p : points) {
|
||||
coords[c++] = p.getLongitude();
|
||||
coords[c++] = p.getLatitude();
|
||||
}
|
||||
this.geometry = new LineString(coordFactory.create(coords, 2), geomFactory);
|
||||
}
|
||||
|
||||
public LineDrawable(double[] lonLat, Style style) {
|
||||
this(new LineString(coordFactory.create(lonLat, 2), geomFactory), style);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
/**
|
||||
* Use this class to draw points and circles which size has to be specified in
|
||||
* screen units. Points (and circles resulting from this class) do not have
|
||||
* area,
|
||||
* however during rendering to make the point visible at varying zoom levels
|
||||
* a buffer area is built around it.
|
||||
* To give the point custom size, create a GeometryBuffer with, set buffer to
|
||||
* your value and assign the point the final style.
|
||||
*
|
||||
* Note that since points do not have any area, they are not generalized.
|
||||
*
|
||||
* Normally points retain their size in the screen units across all zoom levels
|
||||
* but this can be customized. Use setStartLevel on the point's style to specify
|
||||
* from which zoom level the point should "stick to the map" and not decrease in
|
||||
* size.
|
||||
*/
|
||||
public class PointDrawable extends JtsDrawable {
|
||||
|
||||
public PointDrawable(GeoPoint point) {
|
||||
this(point, Style.defaultStyle());
|
||||
}
|
||||
|
||||
public PointDrawable(GeoPoint point, Style style) {
|
||||
this(point.getLongitude(), point.getLatitude(), style);
|
||||
}
|
||||
|
||||
public PointDrawable(double lat, double lon, Style style) {
|
||||
super(style);
|
||||
this.geometry = new GeomBuilder().points(lon, lat).toPoint();
|
||||
}
|
||||
}
|
116
vtm/src/org/oscim/layers/vector/geometries/PolygonDrawable.java
Normal file
116
vtm/src/org/oscim/layers/vector/geometries/PolygonDrawable.java
Normal file
@ -0,0 +1,116 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
|
||||
/**
|
||||
* Predefined class to draw polygons on the map.
|
||||
*/
|
||||
public class PolygonDrawable extends JtsDrawable {
|
||||
|
||||
/**
|
||||
* Creates a polygon using a JTS geometry and a FillableGeometryStyle
|
||||
*
|
||||
* @param polygon
|
||||
* @param style
|
||||
*/
|
||||
public PolygonDrawable(Geometry polygon, Style style) {
|
||||
super(style);
|
||||
|
||||
if (polygon.getDimension() != 2)
|
||||
throw new IllegalArgumentException("Geometry not a Polygon");
|
||||
|
||||
this.geometry = polygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a polygon using the coordinates provided in the List
|
||||
*
|
||||
* @param points
|
||||
*/
|
||||
public PolygonDrawable(List<GeoPoint> points) {
|
||||
this(points, Style.defaultStyle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a polygon given the array of GeoPoints and a FillableGeometryStyle
|
||||
*
|
||||
* @param points
|
||||
* @param style
|
||||
*/
|
||||
public PolygonDrawable(Style style, GeoPoint... points) {
|
||||
this(Arrays.asList(points), style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param points
|
||||
* @param style
|
||||
*/
|
||||
public PolygonDrawable(List<GeoPoint> points, Style style) {
|
||||
this(loadPoints(new GeomBuilder(), points).toPolygon(), style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a polygon given the array of GeoPoints for the boundary, the array
|
||||
* of GeoPoints for the hole and outline and fill color and alpha
|
||||
*
|
||||
* @param points
|
||||
* @param holePoints
|
||||
* @param outlineColor
|
||||
* @param outlineAlpha
|
||||
* @param fillColor
|
||||
* @param fillAlpha
|
||||
*/
|
||||
public PolygonDrawable(GeoPoint[] points, GeoPoint[] holePoints, float lineWidth,
|
||||
int lineColor,
|
||||
int fillColor, float fillAlpha) {
|
||||
this(Arrays.asList(points),
|
||||
Arrays.asList(holePoints),
|
||||
lineWidth, lineColor, fillColor, fillAlpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a polygon using the Coordinates provided in the first List, with a
|
||||
* hole build from the Coordinates in the second List and outline and fill -
|
||||
* color and alpha
|
||||
*
|
||||
* @param points
|
||||
* @param holePoints
|
||||
* @param outlineColor
|
||||
* @param outlineAlpha
|
||||
* @param fillColor
|
||||
* @param fillAlpha
|
||||
*/
|
||||
public PolygonDrawable(List<GeoPoint> points, List<GeoPoint> holePoints,
|
||||
float lineWidth, int lineColor, int fillColor, float fillAlpha) {
|
||||
this(points, holePoints, new Style.Builder()
|
||||
.strokeWidth(lineWidth)
|
||||
.strokeColor(lineColor)
|
||||
.fillColor(fillColor)
|
||||
.fillAlpha(fillAlpha)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a polygon from a List of coordinates in the first List, with a
|
||||
* hole from coordinates in the second List and requires a
|
||||
* FillableGeometryStyle for the color information
|
||||
*
|
||||
* @param points
|
||||
* @param holePoints
|
||||
* @param style
|
||||
*/
|
||||
public PolygonDrawable(List<GeoPoint> points, List<GeoPoint> holePoints, Style style) {
|
||||
super(style);
|
||||
GeomBuilder gb = new GeomBuilder();
|
||||
loadPoints(gb, points).ring();
|
||||
loadPoints(gb, holePoints).ring();
|
||||
this.geometry = gb.toPolygon();
|
||||
this.style = style;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.utils.geom.GeomBuilder;
|
||||
|
||||
/**
|
||||
* Predefined class to draw rectangles on the map
|
||||
*/
|
||||
public class RectangleDrawable extends JtsDrawable {
|
||||
|
||||
/**
|
||||
* Creates a Rectangle given the top-left and the bottom-right coordinate of
|
||||
* it
|
||||
*
|
||||
* @param topLeft
|
||||
* @param bottomRight
|
||||
*/
|
||||
public RectangleDrawable(GeoPoint topLeft, GeoPoint bottomRight) {
|
||||
this(topLeft, bottomRight, Style.defaultStyle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Rectangle given the top-left and the bottom-right coordinate of
|
||||
* it
|
||||
*
|
||||
* @param topLeft
|
||||
* @param bottomRight
|
||||
*/
|
||||
public RectangleDrawable(GeoPoint topLeft, GeoPoint bottomRight, Style style) {
|
||||
super(style);
|
||||
geometry = new GeomBuilder()
|
||||
.point(topLeft.getLongitude(), topLeft.getLatitude())
|
||||
.point(bottomRight.getLongitude(), topLeft.getLatitude())
|
||||
.point(bottomRight.getLongitude(), bottomRight.getLatitude())
|
||||
.point(topLeft.getLongitude(), bottomRight.getLatitude())
|
||||
.toPolygon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Rectangle given the top-left and the bottom-right coordinate of
|
||||
* it
|
||||
*
|
||||
* @param topLeft
|
||||
* @param bottomRight
|
||||
*/
|
||||
public RectangleDrawable(double minLat, double minLon, double maxLat, double maxLon, Style style) {
|
||||
super(style);
|
||||
geometry = new GeomBuilder()
|
||||
.point(minLon, minLat)
|
||||
.point(minLon, maxLat)
|
||||
.point(maxLon, maxLat)
|
||||
.point(maxLon, minLat)
|
||||
.toPolygon();
|
||||
}
|
||||
}
|
198
vtm/src/org/oscim/layers/vector/geometries/Style.java
Normal file
198
vtm/src/org/oscim/layers/vector/geometries/Style.java
Normal file
@ -0,0 +1,198 @@
|
||||
package org.oscim.layers.vector.geometries;
|
||||
|
||||
import org.oscim.backend.canvas.Color;
|
||||
|
||||
/**
|
||||
* Class encapsulating style information for drawing geometries on the map.
|
||||
*/
|
||||
public class Style {
|
||||
|
||||
public static final int GENERALIZATION_HIGH = 1 << 3;
|
||||
public static final int GENERALIZATION_MEDIUM = 1 << 2;
|
||||
public static final int GENERALIZATION_SMALL = 1 << 0;
|
||||
public static final int GENERALIZATION_NONE = 0;
|
||||
|
||||
public final float strokeWidth;
|
||||
public final int strokeColor;
|
||||
|
||||
public final int fillColor;
|
||||
public final float fillAlpha;
|
||||
|
||||
public final double buffer;
|
||||
public final int scalingZoomLevel;
|
||||
|
||||
public final int generalization;
|
||||
|
||||
private Style(Builder builder) {
|
||||
strokeWidth = builder.strokeWidth;
|
||||
strokeColor = builder.strokeColor;
|
||||
|
||||
fillColor = builder.fillColor;
|
||||
fillAlpha = builder.fillAlpha;
|
||||
|
||||
buffer = builder.buffer;
|
||||
scalingZoomLevel = builder.scalingZoomLevel;
|
||||
|
||||
generalization = builder.generalization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Geometry style builder. Usage example:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* Style style = Style.builder()
|
||||
* .strokeWidth(1f).strokeColor(Color.BLACK).build();
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private float strokeWidth = 1f;
|
||||
private int strokeColor = Color.GRAY;
|
||||
private int fillColor = Color.GRAY;
|
||||
private float fillAlpha = 0.25f;
|
||||
|
||||
private double buffer = 1;
|
||||
private int scalingZoomLevel = 1;
|
||||
|
||||
private int generalization = GENERALIZATION_NONE;
|
||||
|
||||
protected Builder() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the GeometryStyle from the specified parameters.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Style build() {
|
||||
return new Style(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line width for the geometry's line or outline.
|
||||
*
|
||||
* @param lineWidth
|
||||
* @return
|
||||
*/
|
||||
public Builder strokeWidth(float lineWidth) {
|
||||
this.strokeWidth = lineWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color for the geometry's line or outline.
|
||||
*
|
||||
* @param stokeColor
|
||||
* @return
|
||||
*/
|
||||
public Builder strokeColor(int stokeColor) {
|
||||
this.strokeColor = stokeColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color for the geometry's area.
|
||||
*
|
||||
* @param fillColor
|
||||
* @return
|
||||
*/
|
||||
public Builder fillColor(int fillColor) {
|
||||
this.fillColor = fillColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets alpha channel value for the geometry's area.
|
||||
*
|
||||
* @param fillAlpha
|
||||
* @return
|
||||
*/
|
||||
public Builder fillAlpha(double fillAlpha) {
|
||||
this.fillAlpha = (float) fillAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function has effect only on Points:
|
||||
* use it to control the size on the circle that
|
||||
* will be built from a buffer around the point.
|
||||
*
|
||||
* @param buffer
|
||||
* @return itself
|
||||
*/
|
||||
public Builder buffer(double buffer) {
|
||||
this.buffer = buffer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function has effect only on Points:
|
||||
* use it to specify from which zoom level the point
|
||||
* should stop decreasing in size and "stick to the map".
|
||||
*
|
||||
* @param zoomlvl
|
||||
*/
|
||||
public Builder scaleZoomLevel(int zoomlvl) {
|
||||
this.scalingZoomLevel = zoomlvl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets generalization factor for the geometry.
|
||||
* Use predefined GeometryStyle.GENERALIZATION_HIGH,
|
||||
* GENERALIZATION_MEDIUM or GENERALIZATION_SMALL
|
||||
*
|
||||
* @param generalization
|
||||
* @return
|
||||
*/
|
||||
public Builder generalization(int generalization) {
|
||||
this.generalization = generalization;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public float getStrokeWidth() {
|
||||
return strokeWidth;
|
||||
}
|
||||
|
||||
public int getStrokeColor() {
|
||||
return strokeColor;
|
||||
}
|
||||
|
||||
public int getFillColor() {
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
public float getFillAlpha() {
|
||||
return fillAlpha;
|
||||
}
|
||||
|
||||
public int getGeneralization() {
|
||||
return generalization;
|
||||
}
|
||||
|
||||
public double getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public int getScalingZoomLevel() {
|
||||
return scalingZoomLevel;
|
||||
}
|
||||
|
||||
static final Style DEFAULT_STYLE = new Builder()
|
||||
.fillColor(0xcccccccc)
|
||||
.fillAlpha(1)
|
||||
.build();
|
||||
|
||||
public static Style defaultStyle() {
|
||||
return DEFAULT_STYLE;
|
||||
}
|
||||
|
||||
public static Style.Builder builder() {
|
||||
return new Style.Builder();
|
||||
}
|
||||
|
||||
}
|
@ -146,7 +146,8 @@ public abstract class SimpleWorker<T> implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup(mTaskTodo);
|
||||
if (mTaskTodo != null)
|
||||
cleanup(mTaskTodo);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
453
vtm/src/org/oscim/utils/geom/GeomBuilder.java
Normal file
453
vtm/src/org/oscim/utils/geom/GeomBuilder.java
Normal file
@ -0,0 +1,453 @@
|
||||
/* Copyright 2013 The jeo project. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.oscim.utils.geom;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.vividsolutions.jts.geom.Coordinate;
|
||||
import com.vividsolutions.jts.geom.Geometry;
|
||||
import com.vividsolutions.jts.geom.GeometryCollection;
|
||||
import com.vividsolutions.jts.geom.GeometryFactory;
|
||||
import com.vividsolutions.jts.geom.LineString;
|
||||
import com.vividsolutions.jts.geom.LinearRing;
|
||||
import com.vividsolutions.jts.geom.MultiLineString;
|
||||
import com.vividsolutions.jts.geom.MultiPoint;
|
||||
import com.vividsolutions.jts.geom.MultiPolygon;
|
||||
import com.vividsolutions.jts.geom.Point;
|
||||
import com.vividsolutions.jts.geom.Polygon;
|
||||
import com.vividsolutions.jts.geom.PrecisionModel;
|
||||
|
||||
/**
|
||||
* Builder for geometry objects.
|
||||
* <p>
|
||||
* Example usage:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* GeometryBuilder gb = new GeometryBuilder();
|
||||
*
|
||||
* // create array two 2d points and turn into a line string
|
||||
* gb.points(1,2,3,4,5,6).toLineString();
|
||||
*
|
||||
* // build a polygon with holes
|
||||
* gb.points(0,0,10,0,10,10,0,10,0,0).ring()
|
||||
* .points(4,4,6,4,6,6,4,6,4,4).ring()
|
||||
* .toPolygon();
|
||||
*
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @author Justin Deoliveira, OpenGeo
|
||||
*
|
||||
*/
|
||||
public class GeomBuilder {
|
||||
|
||||
GeometryFactory factory;
|
||||
Deque<Coordinate> cstack = new ArrayDeque<Coordinate>();
|
||||
Deque<Geometry> gstack = new ArrayDeque<Geometry>();
|
||||
|
||||
/**
|
||||
* Constructs a builder with the default geometry factory.
|
||||
*/
|
||||
public GeomBuilder() {
|
||||
this(new GeometryFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a builder with an explicit geometry factory.
|
||||
*/
|
||||
public GeomBuilder(GeometryFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a builder with a specific srid for geometry objects.
|
||||
*/
|
||||
public GeomBuilder(int srid) {
|
||||
this.factory = new GeometryFactory(new PrecisionModel(), srid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a 2d point to the coordinate stack.
|
||||
*/
|
||||
public GeomBuilder point(double x, double y) {
|
||||
cstack.push(new Coordinate(x, y));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a 3d point to the coordinate stack.
|
||||
*/
|
||||
public GeomBuilder pointz(double x, double y, double z) {
|
||||
cstack.push(new Coordinate(x, y, z));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of 2d points to the coordinate stack.
|
||||
*/
|
||||
public GeomBuilder points(double... ord) {
|
||||
if (ord.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Must specify even number of ordinates");
|
||||
}
|
||||
|
||||
for (int i = 0; i < ord.length; i += 2) {
|
||||
point(ord[i], ord[i + 1]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of 3d points to the coordinate stack.
|
||||
*/
|
||||
public GeomBuilder pointsz(double... ord) {
|
||||
if (ord.length % 3 != 0) {
|
||||
throw new IllegalArgumentException("Must specify ordinates as triples");
|
||||
}
|
||||
|
||||
for (int i = 0; i < ord.length; i += 3) {
|
||||
pointz(ord[i], ord[i + 1], ord[i + 2]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Point from the last point on the coordinate stack, and places
|
||||
* the result
|
||||
* on the geometry stack.
|
||||
*/
|
||||
public GeomBuilder point() {
|
||||
gstack.push(factory.createPoint(cpop()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LineString from all points on the coordinate stack, and places
|
||||
* the result
|
||||
* on the geometry stack.
|
||||
*/
|
||||
public GeomBuilder lineString() {
|
||||
gstack.push(factory.createLineString(cpopAll()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LinearRing from all points on the coordinate stack, and places
|
||||
* the result
|
||||
* on the geometry stack.
|
||||
* <p>
|
||||
* If the first and last coordinate on the point stack are not equal an
|
||||
* additional point will be added.
|
||||
* </p>
|
||||
*/
|
||||
public GeomBuilder ring() {
|
||||
Coordinate[] coords = cpopAll();
|
||||
if (coords.length > 1 && !coords[0].equals(coords[coords.length - 1])) {
|
||||
Coordinate[] tmp = new Coordinate[coords.length + 1];
|
||||
System.arraycopy(coords, 0, tmp, 0, coords.length);
|
||||
tmp[tmp.length - 1] = new Coordinate(tmp[0]);
|
||||
coords = tmp;
|
||||
}
|
||||
|
||||
gstack.push(factory.createLinearRing(coords));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Polygon from all LinearRings on the geometry stack and places
|
||||
* the result back
|
||||
* on the geometry stack.
|
||||
*/
|
||||
public GeomBuilder polygon() {
|
||||
if (gstack.isEmpty() || !(gstack.peek() instanceof LinearRing)) {
|
||||
ring();
|
||||
}
|
||||
|
||||
LinearRing[] rings = gpopAll(LinearRing.class);
|
||||
LinearRing outer = rings[0];
|
||||
LinearRing[] inner = null;
|
||||
if (rings.length > 1) {
|
||||
inner = Arrays.copyOfRange(rings, 1, rings.length);
|
||||
}
|
||||
|
||||
gstack.push(factory.createPolygon(outer, inner));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MultiPoint from all coordinates on the coordinate stack,
|
||||
* plaching the result
|
||||
* back on the geometry stack.
|
||||
* <p>
|
||||
* If the coordinate stack is empty this method will consume all Point
|
||||
* geometries on the geometry stack.
|
||||
* </p>
|
||||
*/
|
||||
public GeomBuilder multiPoint() {
|
||||
if (!cstack.isEmpty()) {
|
||||
gstack.push(factory.createMultiPoint(cpopAll()));
|
||||
}
|
||||
else {
|
||||
gstack.push(factory.createMultiPoint(gpopAll(Point.class)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MultiLineString from all LineStrings on the geometry stack and
|
||||
* places the result
|
||||
* back on the geometry stack.
|
||||
*/
|
||||
public GeomBuilder multiLineString() {
|
||||
gstack.push(factory.createMultiLineString(gpopAll(LineString.class)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MultiPolygon from all Polygons on the geometry stack and places
|
||||
* the result
|
||||
* back on the geometry stack.
|
||||
*/
|
||||
public GeomBuilder multiPolygon() {
|
||||
gstack.push(factory.createMultiPolygon(gpopAll(Polygon.class)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GeometryCollection from all Geometries on the geometry stack
|
||||
* and places the result
|
||||
* back on the geometry stack.
|
||||
*/
|
||||
public GeomBuilder collection() {
|
||||
gstack.push(factory.createGeometryCollection(gpopAll(Geometry.class)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffers the geometry at the top of the geometry stack, and places the
|
||||
* result back on the
|
||||
* geometry stack.
|
||||
*/
|
||||
public GeomBuilder buffer(double amt) {
|
||||
gstack.push(gpop(Geometry.class).buffer(amt));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the top of the geometry stack.
|
||||
*/
|
||||
public Geometry get() {
|
||||
return gpop(Geometry.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a Point.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (Point) point().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public Point toPoint() {
|
||||
return point().gpop(Point.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a LineString.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (LineString) lineString().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public LineString toLineString() {
|
||||
return lineString().gpop(LineString.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a LineString.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (LinearRing) ring().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public LinearRing toLinearRing() {
|
||||
return ring().gpop(LinearRing.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a Polygon.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (Polygon) polygon().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public Polygon toPolygon() {
|
||||
return polygon().gpop(Polygon.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a MultiPoint.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (MultiPoint) multiPoint().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public MultiPoint toMultiPoint() {
|
||||
return multiPoint().gpop(MultiPoint.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a MultiLineString.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (MultiLineString) multiLineString().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public MultiLineString toMultiLineString() {
|
||||
return multiLineString().gpop(MultiLineString.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a MultiPolygon.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (MultiPolygon) multiPolygon().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public MultiPolygon toMultiPolygon() {
|
||||
return multiPolygon().gpop(MultiPolygon.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a GEometryCollection.
|
||||
* <p>
|
||||
* This method is equivalent to:
|
||||
*
|
||||
* <pre>
|
||||
* (GeometryCollection) collection().get();
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
*/
|
||||
public GeometryCollection toCollection() {
|
||||
return collection().gpop(GeometryCollection.class);
|
||||
}
|
||||
|
||||
Coordinate cpop() {
|
||||
return cpop(1)[0];
|
||||
}
|
||||
|
||||
Coordinate[] cpop(int n) {
|
||||
if (cstack.size() < n) {
|
||||
throw new IllegalStateException(String.format("Expected %d values on coordinate stack, "
|
||||
+ "but found %d",
|
||||
n,
|
||||
cstack.size()));
|
||||
}
|
||||
|
||||
Coordinate[] c = new Coordinate[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
c[n - i - 1] = cstack.pop();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Coordinate[] cpopAll() {
|
||||
if (cstack.isEmpty()) {
|
||||
throw new IllegalStateException("Coordinate stack is empty");
|
||||
}
|
||||
|
||||
return cpop(cstack.size());
|
||||
}
|
||||
|
||||
<T extends Geometry> T gpop(Class<T> clazz) {
|
||||
return gpop(1, clazz)[0];
|
||||
}
|
||||
|
||||
<T extends Geometry> T[] gpop(int n, Class<T> clazz) {
|
||||
if (gstack.size() < n) {
|
||||
throw new IllegalStateException(String.format("Expected %d values on geometry stack, "
|
||||
+ "but found %d", n, gstack.size()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] l = (T[]) Array.newInstance(clazz, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Object g = gstack.pop();
|
||||
if (!clazz.isInstance(g)) {
|
||||
throw new IllegalStateException(String.format("Expected %s on geometry stack, but "
|
||||
+ "found %s", clazz.getSimpleName(), g.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
l[n - i - 1] = clazz.cast(g);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
<T extends Geometry> T[] gpopAll(Class<T> clazz) {
|
||||
if (gstack.isEmpty()) {
|
||||
throw new IllegalArgumentException("Geometry stack is empty");
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
Iterator<Geometry> it = gstack.iterator();
|
||||
while (it.hasNext() && clazz.isInstance(it.next())) {
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Expected %s on geometry stack",
|
||||
clazz.getSimpleName()));
|
||||
}
|
||||
|
||||
return gpop(n, clazz);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user