diff --git a/settings.gradle b/settings.gradle index 3d9573e1..0237e353 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'vtm-parent' include ':vtm' +include ':vtm-jts' include ':vtm-tests' include ':vtm-extras' include ':vtm-android' diff --git a/vtm-android-example/build.gradle b/vtm-android-example/build.gradle index c534c83d..b6d8379f 100644 --- a/vtm-android-example/build.gradle +++ b/vtm-android-example/build.gradle @@ -4,6 +4,7 @@ dependencies { compile project(':vtm-android') compile project(':vtm-extras') compile project(':vtm-jeo') + compile project(':vtm-jts') compile project(':vtm-themes') compile 'com.android.support:support-v4:24.0.0' compile 'com.android.support:appcompat-v7:24.0.0' diff --git a/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java b/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java index 810d09cf..b5b54aa5 100644 --- a/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java @@ -24,7 +24,7 @@ import java.util.ArrayList; import org.oscim.backend.canvas.Color; import org.oscim.core.MapPosition; import org.oscim.event.Event; -import org.oscim.layers.PathLayer; +import org.oscim.layers.JtsPathLayer; import org.oscim.map.Map.UpdateListener; import android.os.Bundle; @@ -47,7 +47,7 @@ public class PathOverlayActivity extends BitmapTileMapActivity { 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); + JtsPathLayer pathLayer = new JtsPathLayer(mMap, c, 6); mMap.layers().add(pathLayer); mPathLayers.add(pathLayer); } @@ -74,7 +74,7 @@ public class PathOverlayActivity extends BitmapTileMapActivity { mMap.setMapPosition(0, 0, 1 << 2); } - ArrayList mPathLayers = new ArrayList(); + ArrayList mPathLayers = new ArrayList(); void createLayers(float pos) { diff --git a/vtm-jts/build.gradle b/vtm-jts/build.gradle new file mode 100644 index 00000000..7ee9b784 --- /dev/null +++ b/vtm-jts/build.gradle @@ -0,0 +1,18 @@ +apply plugin: 'java' +apply plugin: 'maven' + +dependencies { + compile project(':vtm') + compile 'com.vividsolutions:jts:1.13' +} + +sourceSets { + main.java.srcDirs = ['src'] +} + +// Automated Gradle project deployment to Sonatype OSSRH +if (isReleaseVersion && project.hasProperty("SONATYPE_USERNAME")) { + afterEvaluate { + project.apply from: "${rootProject.projectDir}/deploy.gradle" + } +} diff --git a/vtm-jts/src/org/oscim/layers/JtsPathLayer.java b/vtm-jts/src/org/oscim/layers/JtsPathLayer.java new file mode 100644 index 00000000..0e544f51 --- /dev/null +++ b/vtm-jts/src/org/oscim/layers/JtsPathLayer.java @@ -0,0 +1,204 @@ +/* + * Copyright 2012 osmdroid authors: Viesturs Zarins, Martin Pearman + * Copyright 2012 Hannes Janetzek + * + * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). + * + * 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 . + */ + +package org.oscim.layers; + +import java.util.ArrayList; +import java.util.List; + +import org.oscim.core.GeoPoint; +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.utils.geom.GeomBuilder; + +import com.vividsolutions.jts.geom.LineString; + +/** This class draws a path line in given color. */ +public class JtsPathLayer extends VectorLayer { + + protected final ArrayList mPoints; + + protected Style mStyle; + protected LineDrawable mDrawable; + + public JtsPathLayer(Map map, int lineColor, float lineWidth) { + super(map); + mStyle = Style.builder() + .strokeColor(lineColor) + .strokeWidth(lineWidth) + .build(); + + mPoints = new ArrayList(); + } + + public JtsPathLayer(Map map, int lineColor) { + this(map, lineColor, 2); + } + + public void setStyle(int lineColor, float lineWidth) { + mStyle = Style.builder() + .strokeColor(lineColor) + .strokeWidth(lineWidth) + .build(); + } + + public void clearPath() { + if (!mPoints.isEmpty()) + mPoints.clear(); + + updatePoints(); + } + + public void setPoints(List pts) { + mPoints.clear(); + mPoints.addAll(pts); + updatePoints(); + } + + public void addPoint(GeoPoint pt) { + mPoints.add(pt); + updatePoints(); + } + + public void addPoint(int latitudeE6, int longitudeE6) { + mPoints.add(new GeoPoint(latitudeE6, longitudeE6)); + updatePoints(); + } + + private void updatePoints() { + 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 getPoints() { + return mPoints; + } + + /** + * Draw a great circle. Calculate a point for every 100km along the path. + * + * @param startPoint + * start point of the great circle + * @param endPoint + * end point of the great circle + */ + public void addGreatCircle(GeoPoint startPoint, GeoPoint endPoint) { + synchronized (mPoints) { + + /* get the great circle path length in meters */ + double length = startPoint.distanceTo(endPoint); + + /* add one point for every 100kms of the great circle path */ + int numberOfPoints = (int) (length / 100000); + + addGreatCircle(startPoint, endPoint, numberOfPoints); + } + } + + /** + * Draw a great circle. + * + * @param startPoint + * start point of the great circle + * @param endPoint + * end point of the great circle + * @param numberOfPoints + * number of points to calculate along the path + */ + 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 */ + + 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; + double lon2 = endPoint.getLongitude() * Math.PI / 180; + + double d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + + Math.cos(lat1) * Math.cos(lat2) + * Math.pow(Math.sin((lon1 - lon2) / 2), 2))); + double bearing = Math.atan2( + Math.sin(lon1 - lon2) * Math.cos(lat2), + Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) + * Math.cos(lat2) + * Math.cos(lon1 - lon2)) + / -(Math.PI / 180); + bearing = bearing < 0 ? 360 + bearing : bearing; + + for (int i = 0, j = numberOfPoints + 1; i < j; i++) { + double f = 1.0 / numberOfPoints * i; + double A = Math.sin((1 - f) * d) / Math.sin(d); + double B = Math.sin(f * d) / Math.sin(d); + double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) + * Math.cos(lon2); + double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) + * Math.sin(lon2); + double z = A * Math.sin(lat1) + B * Math.sin(lat2); + + double latN = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); + double lonN = Math.atan2(y, x); + + gb.point(latN / (Math.PI / 180), lonN / (Math.PI / 180)); + } + + setLineString(gb.toLineString()); + } + + public void setLineString(LineString path) { + synchronized (this) { + if (mDrawable != null) + remove(mDrawable); + mDrawable = new LineDrawable(path, mStyle); + add(mDrawable); + } + mWorker.submit(0); + } + + public void setLineString(double[] lonLat) { + synchronized (this) { + if (mDrawable != null) + remove(mDrawable); + mDrawable = new LineDrawable(lonLat, mStyle); + add(mDrawable); + } + mWorker.submit(0); + } + +} diff --git a/vtm/src/org/oscim/layers/vector/JtsConverter.java b/vtm-jts/src/org/oscim/layers/vector/JtsConverter.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/JtsConverter.java rename to vtm-jts/src/org/oscim/layers/vector/JtsConverter.java diff --git a/vtm/src/org/oscim/layers/vector/VectorLayer.java b/vtm-jts/src/org/oscim/layers/vector/VectorLayer.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/VectorLayer.java rename to vtm-jts/src/org/oscim/layers/vector/VectorLayer.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/CircleDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/CircleDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/CircleDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/CircleDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/Drawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/Drawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/Drawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/Drawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/HexagonDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/HexagonDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/HexagonDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/HexagonDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/JtsDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/JtsDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/JtsDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/JtsDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/LineDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/LineDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/LineDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/LineDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/PointDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/PointDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/PointDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/PointDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/PolygonDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/PolygonDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/PolygonDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/PolygonDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/RectangleDrawable.java b/vtm-jts/src/org/oscim/layers/vector/geometries/RectangleDrawable.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/RectangleDrawable.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/RectangleDrawable.java diff --git a/vtm/src/org/oscim/layers/vector/geometries/Style.java b/vtm-jts/src/org/oscim/layers/vector/geometries/Style.java similarity index 100% rename from vtm/src/org/oscim/layers/vector/geometries/Style.java rename to vtm-jts/src/org/oscim/layers/vector/geometries/Style.java diff --git a/vtm/src/org/oscim/utils/geom/GeomBuilder.java b/vtm-jts/src/org/oscim/utils/geom/GeomBuilder.java similarity index 100% rename from vtm/src/org/oscim/utils/geom/GeomBuilder.java rename to vtm-jts/src/org/oscim/utils/geom/GeomBuilder.java diff --git a/vtm-playground/build.gradle b/vtm-playground/build.gradle index 00c97b70..b36d7f24 100644 --- a/vtm-playground/build.gradle +++ b/vtm-playground/build.gradle @@ -5,6 +5,7 @@ dependencies { compile project(':vtm-desktop') compile project(':vtm-extras') compile project(':vtm-jeo') + compile project(':vtm-jts') runtime 'com.squareup.okhttp:okhttp:1.5.2' } diff --git a/vtm-playground/src/org/oscim/test/PathLayerTest.java b/vtm-playground/src/org/oscim/test/PathLayerTest.java index b6f3073a..80e06179 100644 --- a/vtm-playground/src/org/oscim/test/PathLayerTest.java +++ b/vtm-playground/src/org/oscim/test/PathLayerTest.java @@ -8,7 +8,7 @@ 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.layers.JtsPathLayer; import org.oscim.map.Map; import org.oscim.map.Map.UpdateListener; @@ -33,7 +33,7 @@ public class PathLayerTest extends GdxMapApp { }); } - ArrayList mPathLayers = new ArrayList(); + ArrayList mPathLayers = new ArrayList(); void createLayers(float pos, boolean init) { @@ -61,10 +61,10 @@ public class PathLayerTest extends GdxMapApp { pts.add(new GeoPoint(latitude, longitude)); } - PathLayer pathLayer; + JtsPathLayer pathLayer; if (init) { int c = Color.fade(Color.rainbow((float) (lat + 90) / 180), 0.5f); - pathLayer = new PathLayer(mMap, c, 6); + pathLayer = new JtsPathLayer(mMap, c, 6); mMap.layers().add(pathLayer); mPathLayers.add(pathLayer); } else { diff --git a/vtm/build.gradle b/vtm/build.gradle index f55afe2c..9b2c0d40 100644 --- a/vtm/build.gradle +++ b/vtm/build.gradle @@ -4,7 +4,6 @@ apply plugin: 'maven' configurations { providedCompile } dependencies { - compile 'com.vividsolutions:jts:1.13' compile 'org.slf4j:slf4j-api:1.7.21' providedCompile 'com.google.code.findbugs:annotations:2.0.1' providedCompile 'com.squareup.okhttp:okhttp:1.5.2' diff --git a/vtm/src/org/oscim/layers/PathLayer.java b/vtm/src/org/oscim/layers/PathLayer.java index b1a97d1b..47fbcf29 100644 --- a/vtm/src/org/oscim/layers/PathLayer.java +++ b/vtm/src/org/oscim/layers/PathLayer.java @@ -21,30 +21,39 @@ 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.layers.vector.VectorLayer; -import org.oscim.layers.vector.geometries.LineDrawable; -import org.oscim.layers.vector.geometries.Style; +import org.oscim.core.GeometryBuffer; +import org.oscim.core.MapPosition; +import org.oscim.core.MercatorProjection; +import org.oscim.core.Tile; import org.oscim.map.Map; -import org.oscim.utils.geom.GeomBuilder; - -import com.vividsolutions.jts.geom.LineString; +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; /** This class draws a path line in given color. */ -public class PathLayer extends VectorLayer { +public class PathLayer extends Layer { + /** Stores points, converted to the map projection. */ protected final ArrayList mPoints; + protected boolean mUpdatePoints; - protected Style mStyle; - protected LineDrawable mDrawable; + /** Line style */ + LineStyle mLineStyle; + + final Worker mWorker; public PathLayer(Map map, int lineColor, float lineWidth) { super(map); - mStyle = Style.builder() - .strokeColor(lineColor) - .strokeWidth(lineWidth) - .build(); - + mWorker = new Worker(map); + mLineStyle = new LineStyle(lineColor, lineWidth, Cap.BUTT); + mRenderer = new RenderPath(); mPoints = new ArrayList(); } @@ -52,62 +61,63 @@ public class PathLayer extends VectorLayer { this(map, lineColor, 2); } - public void setStyle(int lineColor, float lineWidth) { - mStyle = Style.builder() - .strokeColor(lineColor) - .strokeWidth(lineWidth) - .build(); - } - public void clearPath() { - if (!mPoints.isEmpty()) - mPoints.clear(); + if (mPoints.isEmpty()) + return; + synchronized (mPoints) { + mPoints.clear(); + } updatePoints(); } public void setPoints(List pts) { - mPoints.clear(); - mPoints.addAll(pts); + synchronized (mPoints) { + mPoints.clear(); + mPoints.addAll(pts); + } updatePoints(); } public void addPoint(GeoPoint pt) { - mPoints.add(pt); + synchronized (mPoints) { + mPoints.add(pt); + } updatePoints(); } public void addPoint(int latitudeE6, int longitudeE6) { - mPoints.add(new GeoPoint(latitudeE6, longitudeE6)); + synchronized (mPoints) { + mPoints.add(new GeoPoint(latitudeE6, longitudeE6)); + } updatePoints(); } private void updatePoints() { - 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); + mWorker.submit(10); + mUpdatePoints = true; } public List 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. - * + * * @param startPoint * start point of the great circle * @param endPoint @@ -128,7 +138,7 @@ public class PathLayer extends VectorLayer { /** * Draw a great circle. - * + * * @param startPoint * start point of the great circle * @param endPoint @@ -137,29 +147,26 @@ public class PathLayer extends VectorLayer { * number of points to calculate along the path */ 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 */ + 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 - GeomBuilder gb = new GeomBuilder(); - - /* convert to radians */ + // convert to radians double lat1 = startPoint.getLatitude() * Math.PI / 180; double lon1 = startPoint.getLongitude() * Math.PI / 180; double lat2 = endPoint.getLatitude() * Math.PI / 180; double lon2 = endPoint.getLongitude() * Math.PI / 180; double d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) - + Math.cos(lat1) * Math.cos(lat2) - * Math.pow(Math.sin((lon1 - lon2) / 2), 2))); + + Math.cos(lat1) * Math.cos(lat2) + * Math.pow(Math.sin((lon1 - lon2) / 2), 2))); double bearing = Math.atan2( - Math.sin(lon1 - lon2) * Math.cos(lat2), - Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) - * Math.cos(lat2) - * Math.cos(lon1 - lon2)) - / -(Math.PI / 180); + Math.sin(lon1 - lon2) * Math.cos(lat2), + Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) + * Math.cos(lat2) + * Math.cos(lon1 - lon2)) + / -(Math.PI / 180); bearing = bearing < 0 ? 360 + bearing : bearing; for (int i = 0, j = numberOfPoints + 1; i < j; i++) { @@ -167,38 +174,237 @@ public class PathLayer extends VectorLayer { double A = Math.sin((1 - f) * d) / Math.sin(d); double B = Math.sin(f * d) / Math.sin(d); double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) - * Math.cos(lon2); + * Math.cos(lon2); double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) - * Math.sin(lon2); + * Math.sin(lon2); double z = A * Math.sin(lat1) + B * Math.sin(lat2); double latN = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); double lonN = Math.atan2(y, x); - - gb.point(latN / (Math.PI / 180), lonN / (Math.PI / 180)); + addPoint((int) (latN / (Math.PI / 180) * 1E6), (int) (lonN / (Math.PI / 180) * 1E6)); } - - setLineString(gb.toLineString()); } - public void setLineString(LineString path) { - synchronized (this) { - if (mDrawable != null) - remove(mDrawable); - mDrawable = new LineDrawable(path, mStyle); - add(mDrawable); + /*** + * 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(); } - mWorker.submit(0); } - public void setLineString(double[] lonLat) { - synchronized (this) { - if (mDrawable != null) - remove(mDrawable); - mDrawable = new LineDrawable(lonLat, mStyle); - add(mDrawable); - } - mWorker.submit(0); + final static class Task { + RenderBuckets bucket = new RenderBuckets(); + MapPosition pos = new MapPosition(); } + final class Worker extends SimpleWorker { + + // 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 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; + } + } }