First of all, thanks a lot for such awesome libraries. It's truly humbling to take a look at complex code and realise how much more I need to learn despite working for more than 20 years in the programming industry.
- I need to add custom polygons to outline custom areas, just like markers. For markers, I use a PathLayer layer.
- So after studying PathLayer source code, I decided to change it slightly in order to use PolygonBuckets instead of LineBuckets.
- It doesn't work :( Been like debugging step by step, opengl and all, for a week. And I don't have a clue why it fails.
Can anybody please take a look and give me any very-welcome advice? I'm so frustrated and ran out of ideas. Find enclosed file GeoShapesLayer.java, which is just like PathLayer.java, only with a TYPE parameter that allows to create Polygon buckets i
package org.oscim.layers;
import android.util.Log;
import org.oscim.backend.canvas.Paint.Cap;
import org.oscim.core.Box;
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.map.Map;
import org.oscim.renderer.BucketRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.bucket.LineBucket;
import org.oscim.renderer.bucket.MeshBucket;
import org.oscim.renderer.bucket.PolygonBucket;
import org.oscim.renderer.bucket.RenderBucket;
import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.theme.styles.AreaStyle;
import org.oscim.theme.styles.LineStyle;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.utils.FastMath;
import org.oscim.utils.async.SimpleWorker;
import org.oscim.utils.geom.LineClipper;
import java.util.ArrayList;
import java.util.List;
import tv.nebular.tribes.config.Conf;
/**
* A Layer that allows to add lines or polygons. Based on PathLayer. Changes are only in the Worker section
* where a PolygonBucket is created rather than a LineBucket if the layer is built for polygons
* (parameter mode in constructor with the constants MODE_XXXX
*/
@SuppressWarnings("unused")
public class GeoShapesLayer extends Layer {
public final static int MODE_LINE = 0, MODE_POLYGON = 1;
private static final String TAG = "GeoShapesLayer";
int mShapeMode;
final ArrayList<GeoPoint> mPoints;
boolean mUpdatePoints;
LineStyle mLineStyle;
RenderStyle mAreaStyle;
GeoShapesLayer.Worker mWorker;
GeometryBuffer mGeom;
public GeoShapesLayer(Map map, int mode, LineStyle style, RenderStyle areaStyle) {
super(map);
mLineStyle = style;
mPoints = new ArrayList<>();
mRenderer = new RenderPolygon();
mWorker = new GeoShapesLayer.Worker(map);
mAreaStyle = areaStyle;
mShapeMode = mode;
if (Conf.LOG_ON) Log.v(TAG, "Creating Shapes Layer for mode " + mode);
}
public void clearPath() {
if (!mPoints.isEmpty()) {
synchronized (mPoints) {
mPoints.clear();
}
updatePoints();
}
}
public void setPoints(List<GeoPoint> pts) {
synchronized (mPoints) {
mPoints.clear();
mPoints.addAll(pts);
}
updatePoints();
}
public void addPoint(GeoPoint pt) {
synchronized (mPoints) {
mPoints.add(pt);
}
updatePoints();
}
public void addPoint(int latitudeE6, int longitudeE6) {
synchronized (mPoints) {
mPoints.add(new GeoPoint(latitudeE6, longitudeE6));
}
updatePoints();
}
private void updatePoints() {
mWorker.submit(10L);
mUpdatePoints = true;
if (Conf.LOG_ON) Log.v(TAG, "Points updated, total "+mPoints.size());
}
public List<GeoPoint> getPoints() {
return mPoints;
}
final class Worker extends SimpleWorker<GeoShapesLayer.Task> {
private final int max = 2048;
private static final int MIN_DIST = 3;
private double[] mPreprojected = new double[2];
private float[] mPPoints = new float[0];
private final LineClipper mClipper = new LineClipper(-2048.0F, -2048.0F, 2048.0F, 2048.0F);
private int mNumPoints;
public Worker(Map map) {
super(map, 0L, new GeoShapesLayer.Task(), new GeoShapesLayer.Task());
}
public boolean doWork(GeoShapesLayer.Task task) {
int size = mNumPoints;
if (mUpdatePoints) {
ArrayList ll = mPoints;
synchronized (mPoints) {
mUpdatePoints = false;
mNumPoints = size = mPoints.size();
ArrayList zoomlevel = mPoints;
double[] mx = mPreprojected;
if (size * 2 >= mx.length) {
mx = mPreprojected = new double[size * 2];
mPPoints = new float[size * 2];
}
for (int i = 0; i < size; ++i) {
MercatorProjection.project((GeoPoint) zoomlevel.get(i), mx, i);
if (Conf.LOG_ON) Log.v(TAG, "- Point "+i+" result "+mx[i]+","+mx[i+1]+" zoom "+zoomlevel.get(i));
}
}
} else if (mGeom != null) {
GeometryBuffer geometryBuffer = mGeom;
mGeom = null;
size = geometryBuffer.index[0];
double[] preprojected = mPreprojected;
if (size > preprojected.length) {
preprojected = mPreprojected = new double[size * 2];
mPPoints = new float[size * 2];
}
for (int i = 0; i < size; i += 2) {
MercatorProjection.project((double) geometryBuffer.points[i + 1], (double) geometryBuffer.points[i], preprojected, i >> 1);
}
mNumPoints = size >>= 1;
}
// preprojected buffer now created
if (size == 0) {
if (task.bucket.get() != null) {
task.bucket.clear();
mMap.render();
}
return true;
} else {
RenderBucket bucket;
switch (mShapeMode) {
case MODE_MESH:
bucket = task.bucket.getMeshBucket(0);
((MeshBucket) bucket).area = (AreaStyle) mAreaStyle;
break;
case MODE_POLYGON:
bucket = task.bucket.getPolygonBucket(0);
((PolygonBucket) bucket).area = (AreaStyle) mAreaStyle;
break;
case MODE_LINE:
default:
if (mLineStyle.stipple == 0 && mLineStyle.texture == null) {
bucket = task.bucket.getLineBucket(0);
} else {
bucket = task.bucket.getLineTexBucket(0);
}
((LineBucket) bucket).line = mLineStyle;
break;
}
mMap.getMapPosition(task.pos);
int zoom = task.pos.zoomLevel;
task.pos.scale = (double) (1 << zoom);
double mx = task.pos.x;
double my = task.pos.y;
double scale = (double) Tile.SIZE * task.pos.scale;
byte flip = 0;
int maxx = Tile.SIZE << zoom - 1;
// x,y son las coordenadas en el MAPA del punto con respecto al CENTRO DEL PUTISIMO MAPA.
int x = (int) ((mPreprojected[0] - mx) * scale);
int y = (int) ((mPreprojected[1] - my) * scale);
Box box = new Box();
mMap.viewport().getBBox(box, 0);
box.scale(scale);
if (Conf.LOG_ON) Log.v(TAG, "- Visible Map: from "+((int)(box.xmin))+","+((int)(box.ymin*scale))+" to "+((int)(box.xmax))+","+((int)(box.xmax))+" TAMAÑO "+((int)(box.getWidth()))+"x"+((int)(box.getHeight())));
if (Conf.LOG_ON) Log.v(TAG, "- Offset obj r/center "+x+","+y);
if (x > maxx) {
x -= maxx * 2;
flip = -1;
} else if (x < -maxx) {
x += maxx * 2;
flip = 1;
}
// x,y now contain absolute tile coordinates of the poing
float[] projected = mPPoints;
int index = addPoint(projected, 0, x, y);
float prevX = (float) x;
float prevY = (float) y;
float[] segment = null;
switch (mShapeMode) {
case MODE_POLYGON:
for (int j = 2; j < size * 2; j += 2) {
x = (int) ((mPreprojected[j] - mx) * scale);
y = (int) ((mPreprojected[j + 1] - my) * scale);
projected[index++] = (float) x;
projected[index++] = (float) y;
}
GeometryBuffer polygon = new GeometryBuffer();
polygon.startPolygon();
for (int i = 0, l = projected.length; i < l; i += 2) {
polygon.addPoint(projected[i], projected[i + 1]);
}
if (Conf.LOG_ON) Log.v(TAG, "Created polygon " + polygon);
((PolygonBucket) bucket).addPolygon(polygon);
break;
case MODE_MESH:
for (int j = 2; j < size * 2; j += 2) {
x = (int) ((mPreprojected[j] - mx) * scale);
y = (int) ((mPreprojected[j + 1] - my) * scale);
projected[index++] = (float) x;
projected[index++] = (float) y;
}
polygon = new GeometryBuffer();
polygon.startPolygon();
for (int i = 0, l = projected.length; i < l; i += 2) {
polygon.addPoint(projected[i], projected[i + 1]);
}
((MeshBucket) bucket).addMesh(polygon);
break;
case MODE_LINE:
boolean isClosed = false;
mClipper.clipStart((float) x, (float) y);
for (int j = 2; j < size * 2; j += 2) {
x = (int) ((mPreprojected[j] - mx) * scale);
y = (int) ((mPreprojected[j + 1] - my) * scale);
byte 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 (index > 2) {
((LineBucket) bucket).addLine(projected, index, isClosed);
}
mClipper.clipStart((float) x, (float) y);
index = addPoint(projected, 0, x, y);
} else {
int clip = mClipper.clipNext((float) x, (float) y);
if (clip < 1) {
if (index > 2) {
((LineBucket) bucket).addLine(projected, index, isClosed);
}
if (clip < 0) {
segment = mClipper.getLine(segment, 0);
((LineBucket) bucket).addLine(segment, 4, isClosed);
prevX = mClipper.outX2;
prevY = mClipper.outY2;
}
index = 0;
} else {
float dx = (float) x - prevX,
dy = (float) y - prevY;
// add point if far enough (3px)
if (index == 0 || FastMath.absMaxCmp(dx, dy, 3.0F)) {
projected[index++] = prevX = (float) x;
projected[index++] = prevY = (float) y;
if (Conf.LOG_ON) Log.v(TAG, "- Projected point "+((index/2)-1)+" is "+x+","+y+" scale "+scale);
}
}
}
}
if (index > 2) {
((LineBucket) bucket).addLine(projected, index, isClosed);
}
}
mMap.render();
return true;
}
}
public void cleanup(GeoShapesLayer.Task task) {
task.bucket.clear();
}
private int addPoint(float[] points, int i, int x, int y) {
points[i++] = (float) x;
points[i++] = (float) y;
return i;
}
}
static final class Task {
RenderBuckets bucket = new RenderBuckets();
MapPosition pos = new MapPosition();
}
final class RenderPolygon extends BucketRenderer {
private int mCurX = -1;
private int mCurY = -1;
private int mCurZ = -1;
public synchronized void update(GLViewport v) {
int tz = 1 << v.pos.zoomLevel;
int tx = (int) (v.pos.x * (double) tz);
int ty = (int) (v.pos.y * (double) tz);
if (tx != this.mCurX || ty != this.mCurY || tz != this.mCurZ) {
mWorker.submit(100L);
mCurX = tx;
mCurY = ty;
mCurZ = tz;
}
GeoShapesLayer.Task t = mWorker.poll();
if (t != null) {
mMapPosition.copy(t.pos);
this.buckets.set(t.bucket.get());
this.compile();
}
}
}
}