labeling testing

This commit is contained in:
Hannes Janetzek 2013-02-18 09:48:32 +01:00
parent d25d967332
commit 34bffcc15f
16 changed files with 628 additions and 348 deletions

View File

@ -35,6 +35,7 @@ import org.oscim.theme.renderinstruction.Area;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.RenderInstruction;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.LineClipper;
import org.oscim.view.DebugSettings;
import org.oscim.view.MapView;
@ -114,6 +115,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private boolean mDebugDrawPolygons;
boolean mDebugDrawUnmatched;
private final LineClipper mClipper;
public static void setRenderTheme(RenderTheme theme) {
renderTheme = theme;
renderLevels = theme.getLevels();
@ -129,6 +132,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
*/
public TileGenerator(MapView mapView) {
// mMapView = mapView;
mClipper = new LineClipper(0,0,Tile.TILE_SIZE, Tile.TILE_SIZE, true);
}
public void cleanup() {
@ -172,10 +176,10 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
}
if (debug.drawTileFrames) {
//mTagName = new Tag("name", tile.toString(), false);
//mPoiX = Tile.TILE_SIZE >> 1;
//mPoiY = 10;
//TileGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
mTagName = new Tag("name", tile.toString(), false);
mPoiX = Tile.TILE_SIZE >> 1;
mPoiY = 10;
TileGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
mIndices = debugBoxIndex;
if (MapView.enableClosePolygons)
@ -476,7 +480,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
int length = mIndices[i];
if (length < 4)
break;
mLabels = WayDecorator.renderText(mCoords, mTagName.value, text,
mLabels = WayDecorator.renderText(mClipper,mCoords, mTagName.value, text,
offset, length, mLabels);
offset += length;
}

View File

@ -18,123 +18,170 @@ package org.oscim.generator;
import org.oscim.core.Tile;
import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.GeometryUtils;
import org.oscim.utils.LineClipper;
public final class WayDecorator {
public static TextItem renderText(float[] coordinates, String string, Text text,
public static TextItem renderText(LineClipper clipper, float[] coordinates, String string,
Text text,
int pos, int len, TextItem textItems) {
TextItem items = textItems;
TextItem t = null;
// calculate the way name length plus some margin of safety
float wayNameWidth = -1;
float minWidth = Tile.TILE_SIZE / 10;
//int skipPixels = 0;
// get the first way point coordinates
int prevX = (int) coordinates[pos + 0];
int prevY = (int) coordinates[pos + 1];
final int min = 0;
final int max = Tile.TILE_SIZE;
// find way segments long enough to draw the way name on them
for (int i = pos + 2; i < pos + len; i += 2) {
for (int i = pos; i < pos + len - 2; i += 2) {
// get the first way point coordinates
int prevX = (int) coordinates[i + 0];
int prevY = (int) coordinates[i + 1];
byte edge = 0;
clipper.clipStart(prevX, prevY);
// get the current way point coordinates
int curX = (int) coordinates[i];
int curY = (int) coordinates[i + 1];
int curX = (int) coordinates[i + 2];
int curY = (int) coordinates[i + 3];
int clip;
if ((clip = clipper.clipNext(curX, curY)) != 0) {
if (clip < 0) {
prevX = clipper.out[0];
prevY = clipper.out[1];
curX = clipper.out[2];
curY = clipper.out[3];
if (prevX == min)
edge |= 1 << 0;
else if (prevX == max)
edge |= 1 << 1;
if (prevY == min)
edge |= 1 << 2;
else if (prevY == max)
edge |= 1 << 3;
if (curX == min)
edge |= 1 << 4;
else if (curX == max)
edge |= 1 << 5;
if (curY == min)
edge |= 1 << 5;
else if (curY == max)
edge |= 1 << 6;
}
}
int last = i;
// calculate the length of the current segment (Euclidian distance)
float vx = prevX - curX;
float vy = prevY - curY;
if (vx == 0 && vy == 0)
continue;
float a = (float) Math.sqrt(vx * vx + vy * vy);
vx /= a;
vy /= a;
int last = i;
int nextX = 0, nextY = 0;
// only if not cur segment crosses edge
if (edge < (1 << 4)) {
vx /= a;
vy /= a;
// add additional segments if possible
for (int j = last + 2; j < pos + len; j += 2) {
nextX = (int) coordinates[j];
nextY = (int) coordinates[j + 1];
// add additional segments if possible
for (int j = i + 4; j < pos + len; j += 2) {
int nextX = (int) coordinates[j + 0];
int nextY = (int) coordinates[j + 1];
float wx = curX - nextX;
float wy = curY - nextY;
if ((clip = clipper.clipNext(nextX, nextY)) != 0) {
if (clip < 0) {
curX = clipper.out[0];
curY = clipper.out[1];
// TODO break when cur has changed
nextX = clipper.out[2];
nextY = clipper.out[3];
}
}
a = (float) Math.sqrt(wx * wx + wy * wy);
wx /= a;
wy /= a;
float wx = nextX - curX;
float wy = nextY - curY;
if (wx == 0 && wy == 0)
continue;
float ux = vx + wx;
float uy = vy + wy;
float area = GeometryUtils.area(prevX, prevY, curX, curY, nextX, nextY);
float diff = wx * uy - wy * ux;
if (area > 1000) {
//Log.d(">>>", "b: " + string + " " + area );
break;
}
if (diff > 0.1 || diff < -0.1)
break;
a = (float) Math.sqrt(wx * wx + wy * wy);
wx /= a;
wy /= a;
last = j;
curX = nextX;
curY = nextY;
continue;
// avoid adding short segments that add much area
if (area / 2 > a * a) {
//Log.d(">>>", "a: " +string + " " + area + " " + a*a);
break;
}
float ux = vx + wx;
float uy = vy + wy;
float diff = wx * uy - wy * ux;
// maximum angle between segments
if (diff > 0.1 || diff < -0.1) {
//Log.d(">>>", "c: " + string + " " + area );
break;
}
curX = nextX;
curY = nextY;
last = j - 2;
if (clip < 0) {
if (nextX == min)
edge |= 1 << 4;
else if (nextX == max)
edge |= 1 << 5;
if (nextY == min)
edge |= 1 << 6;
else if (nextY == max)
edge |= 1 << 7;
}
}
vx = curX - prevX;
vy = curY - prevY;
a = (float) Math.sqrt(vx * vx + vy * vy);
}
vx = curX - prevX;
vy = curY - prevY;
float segmentLength = a;
if (vx < 0)
vx = -vx;
if (vy < 0)
vy = -vy;
if (edge == 0) {
if (segmentLength < minWidth) {
continue;
}
// minimum segment to label
if (vx + vy < minWidth) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue;
}
if (wayNameWidth < 0) {
wayNameWidth = text.paint.measureText(string);
}
// compare against max segment length
if (wayNameWidth > 0 && vx + vy < wayNameWidth) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue;
}
float segmentLength = (float) Math.sqrt(vx * vx + vy * vy);
//if (skipPixels > 0) {
// skipPixels -= segmentLength;
//
//} else
if (segmentLength < minWidth) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue;
}
if (wayNameWidth < 0) {
if (segmentLength < wayNameWidth * 0.50) {
continue;
}
} else if (wayNameWidth < 0) {
wayNameWidth = text.paint.measureText(string);
}
if (segmentLength < wayNameWidth * 0.50) {
// restart from next node
prevX = (int) coordinates[i];
prevY = (int) coordinates[i + 1];
continue;
}
//float s = (wayNameWidth + 20) / segmentLength;
//float s;
//if (wayNameWidth < segmentLength)
// s = (segmentLength - 10) / segmentLength;
//else
//s = (wayNameWidth + 20) / segmentLength;
//float width, height;
float x1, y1, x2, y2;
if (prevX < curX) {
x1 = prevX;
y1 = prevY;
@ -147,17 +194,6 @@ public final class WayDecorator {
y2 = prevY;
}
//// estimate position of text on path
//width = (x2 - x1) / 2f;
////width += 4 * (width / wayNameWidth);
//x2 = x2 - (width - s * width);
//x1 = x1 + (width - s * width);
//
//height = (y2 - y1) / 2f;
////height += 4 * (height / wayNameWidth);
//y2 = y2 - (height - s * height);
//y1 = y1 + (height - s * height);
TextItem n = TextItem.get();
// link items together
@ -177,15 +213,11 @@ public final class WayDecorator {
t.x2 = x2;
t.y2 = y2;
t.length = (short) segmentLength;
t.edges = edge;
t.next = items;
items = t;
// skip to last
i = last;
// store the previous way point coordinates
prevX = curX;
prevY = curY;
}
return items;
}

View File

@ -544,7 +544,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
td.cnt = 0;
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible) {
if (t.isVisible && t.state == STATE_READY) {
t.lock();
td.tiles[td.cnt++] = t;
}

View File

@ -91,6 +91,8 @@ public class TextureObject {
* @param to the TextureObjet to compile and upload
*/
public static synchronized void uploadTexture(TextureObject to) {
// FIXME what needs synchronized ?
if (TextureRenderer.debug)
Log.d(TAG, "upload texture " + to.id);

View File

@ -158,11 +158,12 @@ public final class TextureRenderer {
+ "const float coord_scale = 0.125;"
+ "void main() {"
+ " vec4 pos;"
+ " vec2 dir = vertex.zw;"
+ " if (mod(vertex.x, 2.0) == 0.0){"
+ " pos = u_proj * (u_mv * vec4(vertex.xy + vertex.zw * u_scale, 0.0, 1.0));"
+ " pos = u_proj * (u_mv * vec4(vertex.xy + dir * u_scale, 0.0, 1.0));"
+ " } else {" // place as billboard
+ " vec4 dir = u_mv * vec4(vertex.xy, 0.0, 1.0);"
+ " pos = u_proj * (dir + vec4(vertex.zw * (coord_scale * u_swidth), 0.1, 0.0));"
+ " vec4 center = u_mv * vec4(vertex.xy, 0.0, 1.0);"
+ " pos = u_proj * (center + vec4(dir * (coord_scale * u_swidth), 0.1, 0.0));"
+ " }"
+ " gl_Position = pos;"
+ " tex_c = tex_coord * div;"

View File

@ -290,7 +290,7 @@ public class ExtrusionLayer extends Layer {
}
/* check if face is within tile */
if (!mClipper.clipNext((int) nx, (int) ny)) {
if (mClipper.clipNext((int) nx, (int) ny) == 0) {
even = (even == 0 ? 1 : 0);
continue;
}

View File

@ -15,7 +15,6 @@
package org.oscim.renderer.layer;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.OBB2D;
import android.util.Log;
@ -40,7 +39,33 @@ public class TextItem {
pool = pool.next;
ti.next = null;
ti.active = 0;
//ti.active = 0;
return ti;
}
}
public static TextItem copy(TextItem orig) {
synchronized (lock) {
TextItem ti = pool;
if (ti == null) {
count++;
ti = new TextItem();
} else {
inPool--;
pool = pool.next;
}
ti.next = null;
ti.x = orig.x;
ti.y = orig.y;
ti.x1 = orig.x1;
ti.y1 = orig.y1;
ti.x2 = orig.x2;
ti.y2 = orig.y2;
return ti;
}
}
@ -115,28 +140,24 @@ public class TextItem {
public TextItem move(TextItem ti, float dx, float dy) {
this.x = dx + ti.x;
this.y = dy + ti.y;
this.string = ti.string;
this.text = ti.text;
this.width = ti.width;
this.length = ti.length;
return this;
}
/* copy properties from 'ti' and add offset
*
* */
public TextItem move(TextItem ti, float dx, float dy, float scale) {
this.x = dx + (ti.x * scale);
this.y = dy + (ti.y * scale);
this.x = (dx + ti.x) * scale;
this.y = (dy + ti.y) * scale;
return this;
}
public void clone(TextItem ti){
this.string = ti.string;
this.text = ti.text;
this.width = ti.width;
this.length = ti.length;
return this;
}
public void setAxisAlignedBBox(){
public void setAxisAlignedBBox() {
this.x1 = x - width / 2;
this.y1 = y - text.fontHeight / 2;
this.x2 = x + width / 2;
@ -197,8 +218,10 @@ public class TextItem {
public TextItem n1;
public TextItem n2;
public byte origin;
public byte edges;
public int active;
public OBB2D bbox;
@Override
public String toString() {
return x + " " + y + " " + string;
}
}

View File

@ -34,7 +34,6 @@ public final class TextLayer extends TextureLayer {
public TextItem labels;
private final Canvas mCanvas;
private float mScale;
public TextItem getLabels() {
return labels;
@ -44,11 +43,6 @@ public final class TextLayer extends TextureLayer {
type = Layer.SYMBOL;
mCanvas = new Canvas();
fixed = true;
mScale = 1;
}
public void setScale(float scale) {
mScale = scale;
}
public boolean removeText(TextItem item) {
@ -132,7 +126,7 @@ public final class TextLayer extends TextureLayer {
for (TextItem it = labels; it != null;) {
float width = it.width + 2 * mFontPadX;
float height = (int) (it.text.fontHeight) + 2 * mFontPadY + 0.5f;
float height = (int) (it.text.fontHeight) + 0.5f;
if (height > advanceY)
advanceY = (int) height;
@ -158,7 +152,8 @@ public final class TextLayer extends TextureLayer {
}
}
yy = y + (height - 1) - it.text.fontDescent - mFontPadY;
//yy = y + (height - 1) - it.text.fontDescent - mFontPadY;
yy = y + height - it.text.fontDescent; // - mFontPadY;
if (it.text.stroke != null)
mCanvas.drawText(it.string, x + it.width / 2, yy, it.text.stroke);
@ -174,11 +169,10 @@ public final class TextLayer extends TextureLayer {
float hh2 = 0;
if (!it.text.caption) {
hw /= mScale;
hh2 = hh + it.text.fontDescent / 2;
hh -= it.text.fontDescent / 2;
hh /= mScale;
hh2 /= mScale;
// displace by baseline
float desc = it.text.fontDescent / 2;
hh2 = hh + desc;
hh = hh - desc;
}
// texture coordinates
@ -192,17 +186,17 @@ public final class TextLayer extends TextureLayer {
short x1, x2, x3, x4, y1, y3, y2, y4;
if (it.text.caption) {
if (it.origin == 0) {
x1 = x3 = (short) (SCALE * -hw);
x2 = x4 = (short) (SCALE * hw);
y1 = y2 = (short) (SCALE * hh);
y3 = y4 = (short) (SCALE * -hh);
} else {
x1 = x3 = (short) (SCALE * 0);
x2 = x4 = (short) (SCALE * width);
y1 = y2 = (short) (SCALE * 0);
y3 = y4 = (short) (SCALE * -height);
}
//if (it.origin == 0) {
x1 = x3 = (short) (SCALE * -hw);
x2 = x4 = (short) (SCALE * hw);
y1 = y2 = (short) (SCALE * hh);
y3 = y4 = (short) (SCALE * -hh);
//} else {
// x1 = x3 = (short) (SCALE * 0);
// x2 = x4 = (short) (SCALE * width);
// y1 = y2 = (short) (SCALE * 0);
// y3 = y4 = (short) (SCALE * -height);
//}
} else {
float vx = it.x1 - it.x2;
float vy = it.y1 - it.y2;
@ -219,12 +213,16 @@ public final class TextLayer extends TextureLayer {
vx *= hw;
vy *= hw;
// top-left
x1 = (short) (SCALE * (vx - ux));
y1 = (short) (SCALE * (vy - uy));
// top-right
x2 = (short) (SCALE * (-vx - ux));
y2 = (short) (SCALE * (-vy - uy));
// bot-right
x4 = (short) (SCALE * (-vx + ux2));
y4 = (short) (SCALE * (-vy + uy2));
// bot-left
x3 = (short) (SCALE * (vx + ux2));
y3 = (short) (SCALE * (vy + uy2));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012 Hannes Janetzek
* Copyright 2012, 2013 OpenScienceMap
*
* 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
@ -17,12 +17,15 @@ package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject;
import org.oscim.renderer.TextureRenderer;
import android.util.Log;
/**
* @author Hannes Janetzek
*/
public abstract class TextureLayer extends Layer {
// holds textures and offset in vbo
public TextureObject textures;
// scale mode
public boolean fixed;
/**
@ -30,12 +33,11 @@ public abstract class TextureLayer extends Layer {
* buffer to add vertices
*/
void compile(ShortBuffer sbuf) {
if (TextureRenderer.debug)
Log.d("...", "compile");
for (TextureObject to = textures; to != null; to = to.next)
TextureObject.uploadTexture(to);
// add vertices to vbo
Layers.addPoolItems(this, sbuf);
}

View File

@ -363,7 +363,6 @@ public class TextOverlay extends BasicOverlay {
}
// draw text to bitmaps and create vertices
tl.setScale(scale);
tl.prepare();
// everything synchronized?

View File

@ -30,7 +30,6 @@ import java.util.HashMap;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.generator.JobTile;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices;
@ -82,7 +81,7 @@ public class TextOverlayExp extends BasicOverlay {
@Override
protected void doWork() {
SystemClock.sleep(300);
SystemClock.sleep(150);
if (!mRun)
return;
@ -106,35 +105,45 @@ public class TextOverlayExp extends BasicOverlay {
}
}
private float mSquareRadius;
private int mRelabelCnt;
public TextOverlayExp(MapView mapView) {
super(mapView);
mMapViewPosition = mapView.getMapViewPosition();
layers.textureLayers = new TextLayer();
mTmpLayer = new TextLayer();
mActiveTiles = new HashMap<MapTile, Link>();
mTmpPos = new MapPosition();
mThread = new LabelThread();
mThread.start();
mRelabelCnt = 0;
}
private HashMap<MapTile, PlacementItem> mItemMap;
private HashMap<MapTile, Label> mItemMap;
class PlacementItem extends TextItem {
int tileX;
int tileY;
class Label extends TextItem {
TextItem item;
boolean isTileNeighbour(PlacementItem other) {
int dx = other.tileX - tileX;
if (dx > 1 || dx < -1)
return false;
Link blocking;
Link blockedBy;
int dy = other.tileY - tileY;
if (dy > 1 || dy < -1)
return false;
// shared list of all label for a tile
Link siblings;
return true;
}
MapTile tile;
public byte origin;
public int active;
public OBB2D bbox;
}
class Link {
Link next;
Link prev;
Label it;
}
//private static void setOBB(TextItem ti){
@ -147,35 +156,35 @@ public class TextOverlayExp extends BasicOverlay {
//}
// local pool, avoids synchronized TextItem.get()/release()
private TextItem mPool;
private Label mPool;
private byte checkOverlap(TextLayer tl, TextItem ti) {
private Label mNewLabels;
private Label mPrevLabels;
private final HashMap<MapTile, Link> mActiveTiles;
for (TextItem lp = tl.labels; lp != null;) {
private byte checkOverlap(TextLayer tl, Label ti) {
for (Label lp = (Label) tl.labels; lp != null;) {
// check bounding box
if (!TextItem.bboxOverlaps(ti, lp, 100)) {
lp = lp.next;
if (!TextItem.bboxOverlaps(ti, lp, 150)) {
lp = (Label) lp.next;
continue;
}
if (lp.text == ti.text && (lp.string == ti.string || lp.string.equals(ti.string))) {
if (lp.active <= ti.active)
return 1;
// make strings unique
ti.string = lp.string;
//p.active < ti.active ||
//Log.d(TAG, "overlap, same label in bbox " + lp.string
// + " at " + ti.x + ":" + ti.y + ", " + lp.x + ":"
//+ lp.y + " " + ti.length + "/" + lp.length);
if (lp.length < ti.length) {
Label tmp = lp;
lp = (Label) lp.next;
//if (lp.length > ti.length) {
//Log.d(TAG, "drop " + lp.string);
TextItem tmp = lp;
lp = lp.next;
TextItem.release(tmp.item);
tl.removeText(tmp);
tmp.next = mPool;
@ -199,19 +208,20 @@ public class TextOverlayExp extends BasicOverlay {
boolean intersect = ti.bbox.overlaps(lp.bbox);
// byte intersect = GeometryUtils.linesIntersect(
// ti.x1, ti.y1, ti.x2, ti.y2,
// lp.x1, lp.y1, lp.x2, lp.y2);
if (intersect) {
if (lp.active <= ti.active)
return 1;
//Log.d(TAG, "intersection " + lp.string + " <> " + ti.string
// + " at " + ti.x + ":" + ti.y);
if (!lp.text.caption
&& (lp.text.priority > ti.text.priority || lp.length < ti.length)) {
TextItem tmp = lp;
lp = lp.next;
Label tmp = lp;
lp = (Label) lp.next;
TextItem.release(tmp.item);
tl.removeText(tmp);
tmp.next = mPool;
@ -221,42 +231,66 @@ public class TextOverlayExp extends BasicOverlay {
}
return 1;
// 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 = (Label) lp.next;
}
return 0;
}
private int mMinX;
private int mMinY;
private int mMaxX;
private int mMaxY;
private boolean isVisible(TextItem ti) {
private boolean nodeIsVisible(TextItem ti) {
// rough filter
float dist = ti.x * ti.x + ti.y * ti.y;
if (dist > mSquareRadius)
return false;
return true;
}
private boolean wayIsVisible(TextItem ti) {
// rough filter
float dist = ti.x * ti.x + ti.y * ti.y;
if (dist < mSquareRadius)
return true;
dist = ti.x1 * ti.x1 + ti.y1 * ti.y1;
if (dist < mSquareRadius)
return true;
dist = ti.x2 * ti.x2 + ti.y2 * ti.y2;
if (dist < mSquareRadius)
return true;
return false;
}
private Layers mDebugLayer;
private final float[] mMVP = new float[16];
void addTile(MapTile t) {
}
private static void addDebugLayers(Layers dbg){
dbg.clear();
LineLayer ll = (LineLayer) dbg.getLayer(0, Layer.LINE);
ll.line = new Line((Color.BLUE & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(3, Layer.LINE);
ll.line = new Line((Color.YELLOW & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(1, Layer.LINE);
ll.line = new Line((Color.RED & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(2, Layer.LINE);
ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(4, Layer.LINE);
ll.line = new Line((Color.CYAN & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(5, Layer.LINE);
ll.line = new Line((Color.MAGENTA & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
}
boolean updateLabels() {
if (mTmpLayer == null)
return false;
@ -271,106 +305,133 @@ public class TextOverlayExp extends BasicOverlay {
TextLayer tl = mTmpLayer;
mTmpLayer = null;
mNewLabels = null;
Layers dbg = null; //new Layers();
float[] coords = mTmpCoords;
MapPosition pos = mTmpPos;
synchronized (mMapViewPosition) {
mMapViewPosition.getMapPosition(mTmpPos);
mMapViewPosition.getMapPosition(pos);
mMapViewPosition.getMapViewProjection(coords);
mMapViewPosition.getMatrix(null, null, mMVP);
}
int mw = (mMapView.getWidth() + Tile.TILE_SIZE) / 2;
int mh = (mMapView.getHeight() + Tile.TILE_SIZE) / 2;
mSquareRadius = mw * mw + mh * mh;
// mTiles might be from another zoomlevel than the current:
// this scales MapPosition to the zoomlevel of mTiles...
// TODO create a helper function in MapPosition
MapTile[] tiles = mTileSet.tiles;
int diff = tiles[0].zoomLevel - mTmpPos.zoomLevel;
int diff = tiles[0].zoomLevel - pos.zoomLevel;
float div = FastMath.pow(diff);
float scale = mTmpPos.scale * div;
float scale = pos.scale * div;
double angle = Math.toRadians(mTmpPos.angle);
double angle = Math.toRadians(pos.angle);
float cos = (float) Math.cos(angle);
float sin = (float) Math.sin(angle);
int maxx = Tile.TILE_SIZE << (mTmpPos.zoomLevel - 1);
int maxx = Tile.TILE_SIZE << (pos.zoomLevel - 1);
TextItem ti2 = null;
Label l = null;
if (dbg != null) {
dbg.clear();
LineLayer ll = (LineLayer) dbg.getLayer(0, Layer.LINE);
ll.line = new Line((Color.BLUE & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(3, Layer.LINE);
ll.line = new Line((Color.YELLOW & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(1, Layer.LINE);
ll.line = new Line((Color.RED & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
ll = (LineLayer) dbg.getLayer(2, Layer.LINE);
ll.line = new Line((Color.GREEN & 0xaaffffff), 1, Cap.BUTT);
ll.width = 2;
}
if (dbg != null)
addDebugLayers(dbg);
for (int i = 0, n = mTileSet.cnt; i < n; i++) {
mRelabelCnt++;
MapTile t = tiles[i];
for (Label lp = mPrevLabels; lp != null; ) {
//l.active = mRelabelCnt;
if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)
// transform screen coordinates to tile coordinates
float s = FastMath.pow(lp.tile.zoomLevel - pos.zoomLevel);
float sscale = pos.scale / s;
if (lp.width > lp.length * sscale){
Log.d(TAG, "- scale " + lp + " " + s + " " + sscale + " " + lp.length + " " + lp.width);
TextItem.release(lp.item);
lp = (Label) lp.next;
continue;
}
float dx = (float) (t.pixelX - mTmpPos.x);
float dy = (float) (t.pixelY - mTmpPos.y);
float dx = (float) (lp.tile.pixelX - pos.x * s);
float dy = (float) (lp.tile.pixelY - pos.y * s);
// flip around date-line
if (dx > maxx) {
if (dx > maxx)
dx = dx - maxx * 2;
} else if (dx < -maxx) {
else if (dx < -maxx)
dx = dx + maxx * 2;
}
dx *= scale;
dy *= scale;
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
lp.move(lp.item, dx, dy, sscale);
// acquire a TextItem to add to TextLayer
if (ti2 == null) {
if (mPool == null)
ti2 = TextItem.get();
else {
ti2 = mPool;
mPool = mPool.next;
ti2.next = null;
}
}
if (!lp.text.caption) {
// set line endpoints relative to view to be able to
// check intersections with label from other tiles
float width = (lp.x2 - lp.x1) / 2f;
float height = (lp.y2 - lp.y1) / 2f;
if (!ti.text.caption)
lp.x2 = (lp.x + width);
lp.x1 = (lp.x - width);
lp.y2 = (lp.y + height);
lp.y1 = (lp.y - height);
if (!wayIsVisible(lp)){
Log.d(TAG, "- visible " + lp);
TextItem.release(lp.item);
lp = (Label) lp.next;
continue;
}
ti2.move(ti, dx, dy, scale);
ti2.setAxisAlignedBBox();
byte overlaps = -1;
if (lp.bbox != null)
lp.bbox.set(lp.x, lp.y, lp.x1, lp.y1,
lp.width + 5, lp.text.fontHeight + 5);
else lp.bbox= new OBB2D(lp.x, lp.y, lp.x1, lp.y1,
lp.width + 5, lp.text.fontHeight + 5);
ti2.bbox = new OBB2D(ti2.x, ti2.y, cos, -sin, ti2.width + 6,
ti2.text.fontHeight + 6, true);
if (dbg != null) {
boolean overlaps = false;
for (TextItem lp = tl.labels; lp != null; lp = lp.next) {
if (!lp.text.caption)
continue;
LineLayer ll;
if (overlaps == 1)
ll = (LineLayer) dbg.getLayer(4, Layer.LINE);
else
ll = (LineLayer) dbg.getLayer(5, Layer.LINE);
if (ti2.bbox.overlaps(lp.bbox)) {
Log.d(TAG, "overlap > " + ti2.string + " " + lp.string);
//if (TextItem.bboxOverlaps(ti2, lp, 4)) {
overlaps = true;
break;
{
float[] points = new float[4];
short[] indices = { 4 };
points[0] = (lp.x - width * sscale);
points[1] = (lp.y - height * sscale);
points[2] = (lp.x + width * sscale);
points[3] = (lp.y + height * sscale);
ll.addLine(points, indices, false);
}
if (lp.bbox != null) {
short[] indices = { 8 };
ll.addLine(lp.bbox.corner, indices, true);
}
}
if (!overlaps) {
tl.addText(ti2);
ti2 = null;
}
overlaps = checkOverlap(tl, lp);
if (overlaps == 0) {
if (s != 1)
Log.d(TAG, s + "add prev label " + lp);
Label tmp = lp;
lp = (Label) lp.next;
tmp.next = null;
tl.addText(tmp);
continue;
}
}
TextItem.release(lp.item);
lp = (Label) lp.next;
}
/* add way labels */
@ -378,20 +439,14 @@ public class TextOverlayExp extends BasicOverlay {
MapTile t = tiles[i];
if (t.state == JobTile.STATE_NONE || t.state == JobTile.STATE_LOADING)
continue;
float dx = (float) (t.pixelX - mTmpPos.x);
float dy = (float) (t.pixelY - mTmpPos.y);
float dx = (float) (t.pixelX - pos.x);
float dy = (float) (t.pixelY - pos.y);
// flip around date-line
if (dx > maxx) {
if (dx > maxx)
dx = dx - maxx * 2;
} else if (dx < -maxx) {
else if (dx < -maxx)
dx = dx + maxx * 2;
}
dx *= scale;
dy *= scale;
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
@ -399,36 +454,41 @@ public class TextOverlayExp extends BasicOverlay {
continue;
// acquire a TextItem to add to TextLayer
if (ti2 == null) {
if (l == null) {
if (mPool == null)
ti2 = TextItem.get();
l = new Label();
else {
ti2 = mPool;
mPool = mPool.next;
ti2.next = null;
l = mPool;
mPool = (Label) mPool.next;
l.next = null;
}
l.active = Integer.MAX_VALUE;
}
// check if path at current scale is long enough for text
if (dbg == null && ti.width > ti.length * scale)
continue;
l.clone(ti);
l.move(ti, dx, dy, scale);
// set line endpoints relative to view to be able to
// check intersections with label from other tiles
float width = (ti.x2 - ti.x1) / 2f;
float height = (ti.y2 - ti.y1) / 2f;
ti2.bbox = null;
l.bbox = null;
l.x2 = (l.x + width);
l.x1 = (l.x - width);
l.y2 = (l.y + height);
l.y1 = (l.y - height);
ti2.move(ti, dx, dy, scale);
ti2.x2 = (ti2.x + width);
ti2.x1 = (ti2.x - width);
ti2.y2 = (ti2.y + height);
ti2.y1 = (ti2.y - height);
if (!wayIsVisible(l))
continue;
byte overlaps = -1;
if (dbg == null || ti.width < ti.length * scale)
overlaps = checkOverlap(tl, ti2);
overlaps = checkOverlap(tl, l);
if (dbg != null) {
@ -451,35 +511,93 @@ public class TextOverlayExp extends BasicOverlay {
{
float[] points = new float[4];
short[] indices = { 4 };
points[0] = (ti2.x - width * scale) / scale;
points[1] = (ti2.y - height * scale) / scale;
points[2] = (ti2.x + width * scale) / scale;
points[3] = (ti2.y + height * scale) / scale;
points[0] = (l.x - width * scale);
points[1] = (l.y - height * scale);
points[2] = (l.x + width * scale);
points[3] = (l.y + height * scale);
ll.addLine(points, indices, false);
}
if (ti2.bbox != null) {
if (l.bbox != null) {
short[] indices = { 8 };
float[] points = new float[8];
for (int p = 0; p < 8; p++)
points[p] = ti2.bbox.corner[p] / scale;
ll.addLine(points, indices, true);
ll.addLine(l.bbox.corner, indices, true);
}
}
if (overlaps == 0) {
ti.active++;
tl.addText(ti2);
ti2.active = ti.active;
ti2 = null;
tl.addText(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
}
}
for (TextItem ti = tl.labels; ti != null; ti = ti.next) {
// scale back to fixed zoom-level. could be done in setMatrix
ti.x /= scale;
ti.y /= scale;
for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i];
float dx = (float) (t.pixelX - pos.x);
float dy = (float) (t.pixelY - pos.y);
// flip around date-line
if (dx > maxx)
dx = dx - maxx * 2;
else if (dx < -maxx)
dx = dx + maxx * 2;
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (!ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null) {
if (mPool == null)
l = new Label();
else {
l = mPool;
mPool = (Label) mPool.next;
l.next = null;
}
l.active = Integer.MAX_VALUE;
}
l.clone(ti);
l.move(ti, dx, dy, scale);
if (!nodeIsVisible(l))
continue;
l.setAxisAlignedBBox();
l.bbox = new OBB2D(l.x, l.y, cos, -sin, l.width + 6,
l.text.fontHeight + 6, true);
boolean overlaps = false;
for (Label lp = (Label) tl.labels; lp != null; lp = (Label) lp.next) {
//if (!lp.text.caption)
// continue;
if (lp.bbox == null) {
lp.bbox = new OBB2D(lp.x, lp.y, lp.x1, lp.y1,
lp.width + 5, lp.text.fontHeight + 5);
}
if (l.bbox.overlaps(lp.bbox)) {
//Log.d(TAG, "overlap > " + ti2.string + " " + lp.string);
//if (TextItem.bboxOverlaps(ti2, lp, 4)) {
overlaps = true;
break;
}
}
if (!overlaps) {
tl.addText(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
}
}
for (Label ti = (Label)tl.labels; ti != null; ti = (Label)ti.next) {
if (ti.text.caption)
continue;
@ -497,19 +615,18 @@ public class TextOverlayExp extends BasicOverlay {
}
// release temporarily used TextItems
if (ti2 != null) {
ti2.next = mPool;
mPool = ti2;
}
if (mPool != null) {
TextItem.release(mPool);
mPool = null;
if (l != null) {
l.next = mPool;
mPool = l;
}
// draw text to bitmaps and create vertices
tl.setScale(scale);
tl.prepare();
// after 'prepare' TextLayer does not need TextItems any longer
mPrevLabels = (Label) tl.labels;
tl.labels = null;
// remove tile locks
GLRenderer.releaseTiles(mTileSet);
@ -530,6 +647,7 @@ public class TextOverlayExp extends BasicOverlay {
mTmpLayer = (TextLayer) layers.textureLayers;
// clear textures and text items from previous layer
layers.textureLayers = null;
layers.clear();
if (mDebugLayer != null) {
@ -589,13 +707,15 @@ public class TextOverlayExp extends BasicOverlay {
GLState.test(false, false);
if (layers.layers != null) {
setMatrix(pos, m);
Matrix.multiplyMM(m.mvp, 0, m.proj, 0, m.mvp,0);
setMatrix(pos, m, true);
//Matrix.multiplyMM(m.mvp, 0, m.proj, 0, m.mvp,0);
for (Layer l = layers.layers; l != null;) {
if (l.type == Layer.POLYGON) {
l = PolygonRenderer.draw(pos, l, m.mvp, true, false);
} else {
l = LineRenderer.draw(pos, l, m.mvp, div, 0, layers.lineOffset);
float scale = pos.scale * div;
l = LineRenderer.draw(pos, l, m.mvp, scale, 0, layers.lineOffset);
}
}
}
@ -603,6 +723,7 @@ public class TextOverlayExp extends BasicOverlay {
setMatrix(pos, m);
for (Layer l = layers.textureLayers; l != null;) {
float scale = (mMapPosition.scale / pos.scale) * div;
l = TextureRenderer.draw(l, scale, m.proj, m.mvp);
}
@ -616,9 +737,9 @@ public class TextOverlayExp extends BasicOverlay {
float x = (float) (oPos.x - curPos.x * div);
float y = (float) (oPos.y - curPos.y * div);
float scale = curPos.scale / div;
GlUtils.setMatrix(m.mvp, x * scale, y * scale,
float scale = (curPos.scale / mMapPosition.scale) / div;
float s = curPos.scale / div;
GlUtils.setMatrix(m.mvp, x * s, y * s,
scale / GLRenderer.COORD_MULTIPLIER);
Matrix.multiplyMM(m.mvp, 0, m.view, 0, m.mvp, 0);

View File

@ -167,7 +167,8 @@ public final class Text extends RenderInstruction {
FontMetrics fm = paint.getFontMetrics();
fontHeight = (float) Math.ceil(Math.abs(fm.bottom) + Math.abs(fm.top));
fontDescent = (float) Math.ceil(Math.abs(fm.descent));
//fontDescent = (float) Math.ceil(Math.abs(fm.descent));
fontDescent = Math.abs(fm.bottom);
}
@Override

View File

@ -991,14 +991,22 @@
<!-- place -->
<rule e="node" k="place" v="*">
<rule e="node" k="*" v="suburb|town|village">
<caption k="name" font-size="17" fill="#2020bb"
<rule e="node" k="*" v="suburb" zoom-max="14">
<caption k="name" font-size="17" fill="#bb2020"
stroke="#ffffff" stroke-width="2.0" />
</rule>
<rule e="node" k="*" v="village" zoom-max="14">
<caption k="name" font-size="17" fill="#2020cc"
stroke="#ffffff" stroke-width="2.0" />
</rule>
<rule e="node" k="*" v="island" zoom-min="10">
<caption k="name" font-style="bold" font-size="20" fill="#000000"
stroke="#ffffff" stroke-width="2.0" />
</rule>
<rule e="node" k="*" v="town">
<caption k="name" font-size="19" fill="#000000"
stroke="#ffffff" stroke-width="2.0" />
</rule>
<rule e="node" k="*" v="city">
<caption k="name" font-size="20" fill="#000000"
stroke="#ffffff" stroke-width="2.0" />

View File

@ -329,5 +329,37 @@ public final class GeometryUtils {
return inside;
}
public static float areaSigned(Point p1, Point p2, Point p3) {
return ((p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y))*0.5f;
}
public static float areaSigned(float ax, float ay, float bx, float by, float cx, float cy) {
return ((ax - cx) * (by - cy) - (bx - cx) * (ay - cy)) * 0.5f;
}
public static float area(float ax, float ay, float bx, float by, float cx, float cy) {
float area = ((ax - cx) * (by - cy) - (bx - cx) * (ay - cy)) * 0.5f;
return area < 0 ? -area : area;
}
public static boolean pointInTri(Point pt, Point p1, Point p2, Point p3) {
boolean inside = false;
boolean p1s = p1.y > pt.y;
boolean p2s = p2.y > pt.y;
boolean p3s = p3.y > pt.y;
if ((p1s != p3s)
&& (pt.x < (p3.x - p1.x) * (pt.y - p1.y) / (p3.y - p1.y) + p1.x))
inside = !inside;
if ((p2s != p1s)
&& (pt.x < (p1.x - p2.x) * (pt.y - p2.y) / (p1.y - p2.y) + p2.x))
inside = !inside;
if ((p3s != p2s)
&& (pt.x < (p2.x - p3.x) * (pt.y - p3.y) / (p2.y - p3.y) + p3.x))
inside = !inside;
return inside;
}
}

View File

@ -15,9 +15,10 @@
package org.oscim.utils;
/**
* @author Hannes Janetzek
* taken from http://en.wikipedia.org/wiki/Cohen%E2%80%93
* Sutherland_algorithm
* from http://en.wikipedia.org/wiki/Cohen%E2%80%93
* Sutherland_algorithm
*
* @adapted by Hannes Janetzek
*/
public class LineClipper {
@ -28,19 +29,35 @@ public class LineClipper {
private static final int BOTTOM = 4; // 0100
private static final int TOP = 8; // 1000
private int xmin, xmax, ymin, ymax;
private final int xmin, xmax, ymin, ymax;
public final int[] out;
public LineClipper(int minx, int miny, int maxx, int maxy) {
this.xmin = minx;
this.ymin = miny;
this.xmax = maxx;
this.ymax = maxy;
this.out = null;
}
public LineClipper(int minx, int miny, int maxx, int maxy, boolean keepResult) {
this.xmin = minx;
this.ymin = miny;
this.xmax = maxx;
this.ymax = maxy;
if (keepResult)
this.out = new int[4];
else
this.out = null;
}
private int mPrevOutcode;
private int mPrevX;
private int mPrevY;
public int outX;
public int outY;
public void clipStart(int x0, int y0) {
mPrevX = x0;
mPrevY = y0;
@ -58,8 +75,13 @@ public class LineClipper {
mPrevOutcode = outcode;
}
public boolean clipNext(int x1, int y1) {
boolean accept;
/**
* @param x1 ...
* @param y1 ...
* @return 0 if not intersection, 1 fully within, -1 clipped (and 'out' set to new points)
*/
public int clipNext(int x1, int y1) {
int accept;
int outcode = INSIDE;
if (x1 < xmin)
@ -73,12 +95,13 @@ public class LineClipper {
if ((mPrevOutcode | outcode) == 0) {
// Bitwise OR is 0. Trivially accept
accept = true;
accept = 1;
} else if ((mPrevOutcode & outcode) != 0) {
// Bitwise AND is not 0. Trivially reject
accept = false;
accept = 0;
} else {
accept = clip(mPrevX, mPrevY, x1, y1, xmin, ymin, xmax, ymax, mPrevOutcode, outcode);
accept = clip(mPrevX, mPrevY, x1, y1, xmin, ymin, xmax, ymax, mPrevOutcode, outcode,
this.out) ? -1 : 0;
}
mPrevOutcode = outcode;
mPrevX = x1;
@ -91,7 +114,7 @@ public class LineClipper {
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
// diagonal from (xmin, ymin) to (xmax, ymax).
private static boolean clip(int x0, int y0, int x1, int y1,
int xmin, int ymin, int xmax, int ymax, int outcode0, int outcode1) {
int xmin, int ymin, int xmax, int ymax, int outcode0, int outcode1, int[] out) {
boolean accept = false;
@ -154,9 +177,12 @@ public class LineClipper {
}
}
}
// TODO could do sth with the result x0...
if (accept && out != null) {
out[0] = x0;
out[1] = y0;
out[2] = x1;
out[3] = y1;
}
return accept;
}
}

View File

@ -158,6 +158,37 @@ public class OBB2D {
computeAxes();
}
public void set(float cx, float cy, float dx, float dy, float width, float height){
float vx = cx - dx;
float vy = cy - dy;
float a = (float) Math.sqrt(vx * vx + vy * vy);
vx /= a;
vy /= a;
float hw = width / 2;
float hh = height / 2;
float ux = vy * hh;
float uy = -vx * hh;
vx *= hw;
vy *= hw;
corner[0] = cx - vx - ux;
corner[1] = cy - vy - uy;
corner[2] = cx + vx - ux;
corner[3] = cy + vy - uy;
corner[4] = cx + vx + ux;
corner[5] = cy + vy + uy;
corner[6] = cx - vx + ux;
corner[7] = cy - vy + uy;
computeAxes();
}
public OBB2D(float cx, float cy, float dx, float dy, float width, float height) {
float vx = cx - dx;