impprove labeling a bit by giving priority to labels that were labeled previously
This commit is contained in:
parent
8046081b42
commit
fc96e5f6e8
@ -39,7 +39,7 @@ public class TextItem {
|
|||||||
pool = pool.next;
|
pool = pool.next;
|
||||||
|
|
||||||
ti.next = null;
|
ti.next = null;
|
||||||
|
ti.active = 0;
|
||||||
return ti;
|
return ti;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,6 +173,5 @@ public class TextItem {
|
|||||||
|
|
||||||
public byte origin;
|
public byte origin;
|
||||||
|
|
||||||
public boolean active;
|
public int active;
|
||||||
// public byte placement
|
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,13 @@ import java.util.HashMap;
|
|||||||
import org.oscim.core.MapPosition;
|
import org.oscim.core.MapPosition;
|
||||||
import org.oscim.core.Tile;
|
import org.oscim.core.Tile;
|
||||||
import org.oscim.generator.JobTile;
|
import org.oscim.generator.JobTile;
|
||||||
|
import org.oscim.renderer.BufferObject;
|
||||||
import org.oscim.renderer.GLRenderer;
|
import org.oscim.renderer.GLRenderer;
|
||||||
|
import org.oscim.renderer.GLState;
|
||||||
|
import org.oscim.renderer.LineRenderer;
|
||||||
import org.oscim.renderer.MapTile;
|
import org.oscim.renderer.MapTile;
|
||||||
import org.oscim.renderer.TileManager;
|
import org.oscim.renderer.PolygonRenderer;
|
||||||
|
import org.oscim.renderer.TextureRenderer;
|
||||||
import org.oscim.renderer.TileSet;
|
import org.oscim.renderer.TileSet;
|
||||||
import org.oscim.renderer.layer.Layer;
|
import org.oscim.renderer.layer.Layer;
|
||||||
import org.oscim.renderer.layer.Layers;
|
import org.oscim.renderer.layer.Layers;
|
||||||
@ -38,6 +42,7 @@ import org.oscim.view.MapView;
|
|||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint.Cap;
|
import android.graphics.Paint.Cap;
|
||||||
|
import android.opengl.GLES20;
|
||||||
import android.opengl.Matrix;
|
import android.opengl.Matrix;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -45,18 +50,17 @@ import android.util.Log;
|
|||||||
public class TextOverlayExp extends BasicOverlay {
|
public class TextOverlayExp extends BasicOverlay {
|
||||||
private final static String TAG = TextOverlayExp.class.getName();
|
private final static String TAG = TextOverlayExp.class.getName();
|
||||||
|
|
||||||
private TileSet mTiles;
|
private TileSet mTileSet;
|
||||||
private LabelThread mThread;
|
private LabelThread mThread;
|
||||||
|
|
||||||
private MapPosition mWorkPos;
|
private MapPosition mTmpPos;
|
||||||
|
|
||||||
// TextLayer that is updating
|
// TextLayer that is updating
|
||||||
private TextLayer mWorkLayer;
|
private TextLayer mTmpLayer;
|
||||||
// TextLayer that is ready to be added to 'layers'
|
// TextLayer that is ready to be added to 'layers'
|
||||||
private TextLayer mCurLayer;
|
private TextLayer mNextLayer;
|
||||||
|
|
||||||
/* package */boolean mRun;
|
/* package */boolean mRun;
|
||||||
/* package */boolean mRerun;
|
|
||||||
|
|
||||||
class LabelThread extends PausableThread {
|
class LabelThread extends PausableThread {
|
||||||
|
|
||||||
@ -67,10 +71,12 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
mRun = false;
|
mRun = false;
|
||||||
if (updateLabels())
|
|
||||||
mMapView.redrawMap(false);
|
if (updateLabels()) {
|
||||||
else
|
mMapView.redrawMap(true);
|
||||||
|
} else {
|
||||||
mRun = true;
|
mRun = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,16 +86,22 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasWork() {
|
protected boolean hasWork() {
|
||||||
return mRun || mRerun;
|
return mRun;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int mSerial;
|
||||||
|
|
||||||
public TextOverlayExp(MapView mapView) {
|
public TextOverlayExp(MapView mapView) {
|
||||||
super(mapView);
|
super(mapView);
|
||||||
|
|
||||||
mWorkPos = new MapPosition();
|
layers.textureLayers = new TextLayer();
|
||||||
|
mTmpLayer = new TextLayer();
|
||||||
|
|
||||||
|
mTmpPos = new MapPosition();
|
||||||
mThread = new LabelThread();
|
mThread = new LabelThread();
|
||||||
mThread.start();
|
mThread.start();
|
||||||
|
mSerial = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashMap<TextItem, PlacementItem> mItemMap;
|
private HashMap<TextItem, PlacementItem> mItemMap;
|
||||||
@ -111,6 +123,7 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// local pool, avoids synchronized TextItem.get()/release()
|
||||||
private TextItem mPool;
|
private TextItem mPool;
|
||||||
|
|
||||||
private byte checkOverlap(TextLayer tl, TextItem ti) {
|
private byte checkOverlap(TextLayer tl, TextItem ti) {
|
||||||
@ -122,7 +135,7 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check bounding box
|
// check bounding box
|
||||||
if (!TextItem.bboxOverlaps(ti, lp, 50)) {
|
if (!TextItem.bboxOverlaps(ti, lp, 100)) {
|
||||||
lp = lp.next;
|
lp = lp.next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -131,7 +144,9 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
// make strings unique
|
// make strings unique
|
||||||
ti.string = lp.string;
|
ti.string = lp.string;
|
||||||
|
|
||||||
if (lp.length > ti.length) {
|
if (lp.active < ti.active) {
|
||||||
|
|
||||||
|
//if (lp.length > ti.length) {
|
||||||
//Log.d(TAG, "drop " + lp.string);
|
//Log.d(TAG, "drop " + lp.string);
|
||||||
TextItem tmp = lp;
|
TextItem tmp = lp;
|
||||||
lp = lp.next;
|
lp = lp.next;
|
||||||
@ -159,24 +174,11 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
lp.x1, lp.y1, lp.x2, lp.y2);
|
lp.x1, lp.y1, lp.x2, lp.y2);
|
||||||
|
|
||||||
if (intersect != 0) {
|
if (intersect != 0) {
|
||||||
//Log.d(TAG, "overlap " + lp.string + " <> " + ti.string
|
//Log.d(TAG, "intersection " + lp.string + " <> " + ti.string
|
||||||
//+ " at " + ti.x + ":" + ti.y);
|
// + " at " + ti.x + ":" + ti.y);
|
||||||
|
|
||||||
if ((lp.n1 != null && lp.n1 == ti.n2) ||
|
if (lp.active < ti.active) {
|
||||||
(lp.n2 != null && lp.n2 == ti.n1)) {
|
//if (lp.length > ti.length) {
|
||||||
//Log.d(TAG, "overlap with adjacent label " + lp.string
|
|
||||||
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":" + lp.y);
|
|
||||||
|
|
||||||
return intersect;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ti.n1 != null || ti.n2 != null) && (lp.n1 == null && lp.n2 == null)) {
|
|
||||||
Log.d(TAG, "overlap, other is unique " + lp.string + " " + ti.string
|
|
||||||
+ " at " + ti.x + ":" + ti.y + ", " + lp.x + ":" + lp.y);
|
|
||||||
return intersect;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lp.length > ti.length) {
|
|
||||||
//Log.d(TAG, "drop " + lp.string);
|
//Log.d(TAG, "drop " + lp.string);
|
||||||
TextItem tmp = lp;
|
TextItem tmp = lp;
|
||||||
lp = lp.next;
|
lp = lp.next;
|
||||||
@ -188,7 +190,24 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersect;
|
return intersect;
|
||||||
|
|
||||||
|
// if ((lp.n1 != null && lp.n1 == ti.n2) ||
|
||||||
|
// (lp.n2 != null && lp.n2 == ti.n1)) {
|
||||||
|
// Log.d(TAG, "overlap with adjacent label " + lp.string
|
||||||
|
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":" + lp.y);
|
||||||
|
//
|
||||||
|
// return intersect;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if ((ti.n1 != null || ti.n2 != null) && (lp.n1 == null && lp.n2 == null)) {
|
||||||
|
// Log.d(TAG, "overlap, other is unique " + lp.string + " " + ti.string
|
||||||
|
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":" + lp.y);
|
||||||
|
// return intersect;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return intersect;
|
||||||
}
|
}
|
||||||
|
|
||||||
lp = lp.next;
|
lp = lp.next;
|
||||||
@ -196,55 +215,42 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Layers mDebugLayer = null; //new Layers();
|
private Layers mDebugLayer;
|
||||||
|
|
||||||
boolean updateLabels() {
|
boolean updateLabels() {
|
||||||
mTiles = TileManager.getActiveTiles(mTiles);
|
if (mTmpLayer == null)
|
||||||
|
|
||||||
// Log.d("...", "relabel " + mRerun + " " + x + " " + y);
|
|
||||||
if (mTiles.cnt == 0)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mMapView.getMapViewPosition().getMapPosition(mWorkPos, null);
|
mTileSet = GLRenderer.getVisibleTiles(mTileSet);
|
||||||
|
|
||||||
TextLayer tl = mWorkLayer;
|
if (mTileSet.cnt == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (tl == null)
|
mSerial++;
|
||||||
tl = new TextLayer();
|
|
||||||
|
TextLayer tl = mTmpLayer;
|
||||||
|
mTmpLayer = null;
|
||||||
|
|
||||||
|
//mDebugLayer = new Layers();
|
||||||
|
|
||||||
// mTiles might be from another zoomlevel than the current:
|
// mTiles might be from another zoomlevel than the current:
|
||||||
// this scales MapPosition to the zoomlevel of mTiles...
|
// this scales MapPosition to the zoomlevel of mTiles...
|
||||||
// TODO create a helper function in MapPosition
|
// TODO create a helper function in MapPosition
|
||||||
int diff = mTiles.tiles[0].zoomLevel - mWorkPos.zoomLevel;
|
mMapView.getMapViewPosition().getMapPosition(mTmpPos, null);
|
||||||
|
// capture current state
|
||||||
|
|
||||||
// only relabel when tiles belong to the current zoomlevel or its parent
|
MapTile[] tiles = mTileSet.tiles;
|
||||||
if (diff > 1 || diff < -2) {
|
|
||||||
// pass back the current layer
|
|
||||||
synchronized (this) {
|
|
||||||
Log.d(TAG, "drop labels: diff " + diff);
|
|
||||||
//mCurLayer = tl;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MapTile[] tiles = mTiles.tiles;
|
int diff = tiles[0].zoomLevel - mTmpPos.zoomLevel;
|
||||||
// for (int i = 0, n = mTiles.cnt; i < n; i++) {
|
|
||||||
// MapTile t = tiles[i];
|
|
||||||
// if (t.isVisible && (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)) {
|
|
||||||
// synchronized (this) {
|
|
||||||
// Log.d(TAG, "tiles not ready");
|
|
||||||
// //mCurLayer = tl;
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
float scale = mWorkPos.scale;
|
float div = FastMath.pow(diff);
|
||||||
double angle = Math.toRadians(mWorkPos.angle);
|
|
||||||
|
float scale = mTmpPos.scale * div;
|
||||||
|
double angle = Math.toRadians(mTmpPos.angle);
|
||||||
float cos = (float) Math.cos(angle);
|
float cos = (float) Math.cos(angle);
|
||||||
float sin = (float) Math.sin(angle);
|
float sin = (float) Math.sin(angle);
|
||||||
|
|
||||||
int maxx = Tile.TILE_SIZE << (mWorkPos.zoomLevel - 1);
|
int maxx = Tile.TILE_SIZE << (mTmpPos.zoomLevel - 1);
|
||||||
|
|
||||||
TextItem ti2 = null;
|
TextItem ti2 = null;
|
||||||
|
|
||||||
@ -263,12 +269,11 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT);
|
ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT);
|
||||||
ll.width = 2;
|
ll.width = 2;
|
||||||
}
|
}
|
||||||
|
int added = 0;
|
||||||
|
|
||||||
|
for (int i = 0, n = mTileSet.cnt; i < n; i++) {
|
||||||
|
|
||||||
// TODO more sophisticated placement :)
|
|
||||||
for (int i = 0, n = mTiles.cnt; i < n; i++) {
|
|
||||||
MapTile t = tiles[i];
|
MapTile t = tiles[i];
|
||||||
if (!t.isVisible)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)
|
if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)
|
||||||
continue;
|
continue;
|
||||||
@ -276,8 +281,8 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
//if (t.joined != MapTile.JOINED)
|
//if (t.joined != MapTile.JOINED)
|
||||||
// joinTile()
|
// joinTile()
|
||||||
|
|
||||||
float dx = (float) (t.pixelX - mWorkPos.x);
|
float dx = (float) (t.pixelX - mTmpPos.x);
|
||||||
float dy = (float) (t.pixelY - mWorkPos.y);
|
float dy = (float) (t.pixelY - mTmpPos.y);
|
||||||
|
|
||||||
// flip around date-line
|
// flip around date-line
|
||||||
if (dx > maxx) {
|
if (dx > maxx) {
|
||||||
@ -378,14 +383,20 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
ll.addLine(points, indices, false);
|
ll.addLine(points, indices, false);
|
||||||
}
|
}
|
||||||
if (overlaps == 0) {
|
if (overlaps == 0) {
|
||||||
|
ti.active++;
|
||||||
|
|
||||||
tl.addText(ti2);
|
tl.addText(ti2);
|
||||||
|
ti2.active = ti.active;
|
||||||
ti2 = null;
|
ti2 = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//int count = 0;
|
int count = 0;
|
||||||
for (TextItem ti = tl.labels; ti != null; ti = ti.next) {
|
for (TextItem ti = tl.labels; ti != null; ti = ti.next) {
|
||||||
//count++;
|
count++;
|
||||||
|
|
||||||
|
//ti.active = mSerial;
|
||||||
|
|
||||||
// scale back to fixed zoom-level. could be done in setMatrix
|
// scale back to fixed zoom-level. could be done in setMatrix
|
||||||
ti.x /= scale;
|
ti.x /= scale;
|
||||||
ti.y /= scale;
|
ti.y /= scale;
|
||||||
@ -419,15 +430,15 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
tl.setScale(scale);
|
tl.setScale(scale);
|
||||||
tl.prepare();
|
tl.prepare();
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
Log.d(TAG, "> no labels: " + count + " " + added);
|
||||||
|
|
||||||
//TextItem.printPool();
|
//TextItem.printPool();
|
||||||
|
|
||||||
// if (count == 0)
|
|
||||||
// Log.d(TAG, "> no labels: " + count);
|
|
||||||
|
|
||||||
//Log.d(TAG, "new labels: " + count);
|
//Log.d(TAG, "new labels: " + count);
|
||||||
// everything synchronized?
|
|
||||||
|
// pass new labels for rendering
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
mCurLayer = tl;
|
mNextLayer = tl;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -435,11 +446,10 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void update(MapPosition curPos, boolean positionChanged,
|
public synchronized void update(MapPosition curPos, boolean positionChanged,
|
||||||
boolean tilesChanged) {
|
boolean tilesChanged) {
|
||||||
// Log.d("...", "update " + tilesChanged + " " + positionChanged);
|
|
||||||
|
|
||||||
if (mCurLayer != null) {
|
if (mNextLayer != null) {
|
||||||
// keep text layer, not recrating its canvas each time
|
// keep text layer, not recrating its canvas each time
|
||||||
mWorkLayer = (TextLayer) layers.textureLayers;
|
mTmpLayer = (TextLayer) layers.textureLayers;
|
||||||
|
|
||||||
// clear textures and text items from previous layer
|
// clear textures and text items from previous layer
|
||||||
layers.clear();
|
layers.clear();
|
||||||
@ -449,32 +459,81 @@ public class TextOverlayExp extends BasicOverlay {
|
|||||||
mDebugLayer.layers = null;
|
mDebugLayer.layers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new TextLayer to be uploaded and used
|
// set new TextLayer to be uploaded and rendered
|
||||||
layers.textureLayers = mCurLayer;
|
layers.textureLayers = mNextLayer;
|
||||||
|
|
||||||
mCurLayer = null;
|
|
||||||
|
|
||||||
// make the 'labeled' MapPosition current
|
// make the 'labeled' MapPosition current
|
||||||
MapPosition tmp = mMapPosition;
|
MapPosition tmp = mMapPosition;
|
||||||
mMapPosition = mWorkPos;
|
mMapPosition = mTmpPos;
|
||||||
mWorkPos = tmp;
|
mTmpPos = tmp;
|
||||||
|
|
||||||
// TODO should return true instead
|
|
||||||
newData = true;
|
newData = true;
|
||||||
|
mNextLayer = null;
|
||||||
|
if (!(positionChanged || tilesChanged))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mHolding)
|
if (mHolding)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (tilesChanged || positionChanged) {
|
if (!mRun) {
|
||||||
if (!mRun) {
|
mRun = true;
|
||||||
mRun = true;
|
synchronized (mThread) {
|
||||||
synchronized (mThread) {
|
mThread.notify();
|
||||||
mThread.notify();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile() {
|
||||||
|
int newSize = layers.getSize();
|
||||||
|
if (newSize == 0)
|
||||||
|
Log.d(TAG, "text layer size " + newSize);
|
||||||
|
|
||||||
|
if (newSize == 0) {
|
||||||
|
BufferObject.release(vbo);
|
||||||
|
vbo = null;
|
||||||
|
isReady = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vbo == null) {
|
||||||
|
vbo = BufferObject.get(0);
|
||||||
|
|
||||||
|
if (vbo == null)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newSize > 0) {
|
||||||
|
if (GLRenderer.uploadLayers(layers, vbo, newSize, true))
|
||||||
|
isReady = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void render(MapPosition pos, float[] mv, float[] proj) {
|
||||||
|
setMatrix(pos, mv);
|
||||||
|
float div = FastMath.pow(mMapPosition.zoomLevel - pos.zoomLevel);
|
||||||
|
|
||||||
|
Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0);
|
||||||
|
|
||||||
|
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo.id);
|
||||||
|
GLState.test(false, false);
|
||||||
|
|
||||||
|
for (Layer l = layers.layers; l != null;) {
|
||||||
|
if (l.type == Layer.POLYGON) {
|
||||||
|
l = PolygonRenderer.draw(pos, l, mvp, true, false);
|
||||||
|
} else {
|
||||||
|
l = LineRenderer.draw(pos, l, mvp, div, 0, layers.lineOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Layer l = layers.textureLayers; l != null;) {
|
||||||
|
l = TextureRenderer.draw(l, (mMapPosition.scale / pos.scale) * div
|
||||||
|
, proj, mv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setMatrix(MapPosition curPos, float[] matrix) {
|
protected void setMatrix(MapPosition curPos, float[] matrix) {
|
||||||
MapPosition oPos = mMapPosition;
|
MapPosition oPos = mMapPosition;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user