- initial way labeling

- add no-projection option for mapdata
- use depth buffer for line clipping to tile - no more scissor, yay!
- extract line and poly render function from maprenderer
- use one vbo for both polys and lines
- use linear interpolator for fling-scroller
- add 'exclusive' negative matcher to rendertheme
- add some more options to rendertheme, kind of inheritance at least for
  lines, see theme
- add caching for node tags -> renderinstructions
- ...
This commit is contained in:
Hannes Janetzek 2012-08-28 09:25:06 +02:00
parent 1cf0c02dd2
commit 2fccf0f214
46 changed files with 3591 additions and 2815 deletions

View File

@ -16,6 +16,7 @@ package org.mapsforge.android;
import org.mapsforge.android.mapgenerator.IMapGenerator;
import org.mapsforge.android.mapgenerator.MapGeneratorJob;
import org.mapsforge.android.rendertheme.RenderTheme;
import android.opengl.GLSurfaceView;
@ -46,4 +47,6 @@ public interface IMapRenderer extends GLSurfaceView.Renderer {
public void redrawTiles(boolean clear);
public IMapGenerator createMapGenerator();
public void setRenderTheme(RenderTheme t);
}

View File

@ -496,6 +496,7 @@ public class MapView extends GLSurfaceView {
*
* @param internalRenderTheme
* the internal rendering theme.
* @return ...
* @throws IllegalArgumentException
* if the supplied internalRenderTheme is null.
*/
@ -538,6 +539,7 @@ public class MapView extends GLSurfaceView {
try {
inputStream = theme.getRenderThemeAsStream();
RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream);
mMapRenderer.setRenderTheme(t);
mMapWorkers[0].getMapGenerator().setRenderTheme(t);
return true;
} catch (ParserConfigurationException e) {

View File

@ -14,21 +14,23 @@
*/
package org.mapsforge.android.glrenderer;
import java.util.ArrayList;
import org.mapsforge.android.mapgenerator.MapTile;
import org.mapsforge.core.Tile;
class GLMapTile extends MapTile {
long lastDraw = 0;
VertexBufferObject vbo;
// polygonOffset is always 8
int lineOffset;
VertexBufferObject lineVBO;
VertexBufferObject polygonVBO;
TextTexture texture;
LineLayer lineLayers;
PolygonLayer polygonLayers;
ArrayList<TextItem> labels;
TextItem labels;
boolean newData;
boolean loading;
@ -37,9 +39,6 @@ class GLMapTile extends MapTile {
final long x;
final long y;
// scissor coordinates
int sx, sy, sw, sh;
final GLMapTile[] child = { null, null, null, null };
GLMapTile parent;

View File

@ -15,38 +15,35 @@
package org.mapsforge.android.glrenderer;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.core.Tile;
import android.graphics.Paint.Cap;
import android.util.FloatMath;
class LineLayer {
private static final float SCALE_FACTOR = 16;
Line line;
private static final float S = MapRenderer.COORD_MULTIPLIER;
private static final float S1000 = 1000;
LineLayer next;
LineLayer outlines;
Line line;
float width;
boolean isOutline;
int layer;
ShortItem pool;
protected ShortItem curItem;
int verticesCnt;
int offset;
short[] mVertex;
final int layer;
LineLayer(int layer, Line line, boolean outline) {
LineLayer(int layer, Line line, float width, boolean outline) {
this.layer = layer;
this.width = width;
this.line = line;
this.isOutline = outline;
if (!outline) {
curItem = ShortPool.get();
pool = curItem;
}
}
void addOutline(LineLayer link) {
@ -58,45 +55,82 @@ class LineLayer {
outlines = link;
}
short[] getNextItem() {
curItem.used = ShortItem.SIZE;
private static ShortItem addTwoVertex(short[] vertex, ShortItem item) {
ShortItem it = item;
curItem.next = ShortPool.get();
curItem = curItem.next;
if (it.used + 6 >= ShortItem.SIZE) {
return curItem.vertices;
if (it.used == ShortItem.SIZE) {
it.next = ShortPool.get();
it = it.next;
} else {
System.arraycopy(vertex, 0, it.vertices, it.used, 6);
it.used += 6;
it.next = ShortPool.get();
it = it.next;
System.arraycopy(vertex, 6, it.vertices, it.used, 6);
it.used += 6;
return it;
}
}
System.arraycopy(vertex, 0, it.vertices, it.used, 12);
it.used += 12;
return it;
}
private static ShortItem addVertex(short[] vertex, ShortItem item) {
ShortItem it = item;
if (it.used == ShortItem.SIZE) {
it.next = ShortPool.get();
it = it.next;
}
System.arraycopy(vertex, 0, it.vertices, it.used, 6);
it.used += 6;
return it;
}
/*
* line extrusion is based on code from GLMap (https://github.com/olofsj/GLMap/) by olofsj
* line extrusion is based on code from GLMap (https://github.com/olofsj/GLMap/) by olofsj -- need some way to know
* how the road connects to set the ending angles
*/
void addLine(float[] pointArray, int pos, int length, float w, boolean capRound) {
void addLine(float[] pointArray, int pos, int length) {
float x, y, nextX, nextY, prevX, prevY, ux, uy, vx, vy, wx, wy;
float a;
int pointPos = pos;
boolean rounded = capRound;
width = w;// * SCALE_FACTOR;
if (w < 0.5)
rounded = false;
boolean rounded = false;
boolean squared = false;
if (line.cap == Cap.ROUND)
rounded = true;
else if (line.cap == Cap.SQUARE)
squared = true;
if (pool == null) {
curItem = ShortPool.get();
pool = curItem;
mVertex = new short[12];
}
// amount of vertices used
verticesCnt += length + (rounded ? 6 : 2);
int MAX = PoolItem.SIZE;
ShortItem si = curItem;
short[] curVertices = curItem.vertices;
int vertexPos = curItem.used;
x = pointArray[pointPos++];
y = pointArray[pointPos++];
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
x = pointArray[pointPos++];// * SCALE_FACTOR;
y = pointArray[pointPos++];// * SCALE_FACTOR;
nextX = pointArray[pointPos++];// * SCALE_FACTOR;
nextY = pointArray[pointPos++];// * SCALE_FACTOR;
nextX = pointArray[pointPos++];
nextY = pointArray[pointPos++];
// Calculate triangle corners for the given width
vx = nextX - x;
@ -110,109 +144,83 @@ class LineLayer {
ux = -vy;
uy = vx;
float uxw = ux * w;
float uyw = uy * w;
float uxw = ux;
float uyw = uy;
float vxw = vx * w;
float vyw = vy * w;
float vxw = vx;
float vyw = vy;
int tsize = Tile.TILE_SIZE;
// boolean outside = (x <= 0 || x >= Tile.TILE_SIZE || y <= 0 || y >= Tile.TILE_SIZE)
// && (x - vxw <= 0 || x - vxw >= Tile.TILE_SIZE || y - vyw <= 0 || y - vyw >= Tile.TILE_SIZE);
short v[] = mVertex;
v[0] = (short) (x * S);
v[1] = (short) (y * S);
boolean outside = (x <= 0 || x >= tsize || y <= 0 || y >= tsize)
&& (x - vxw <= 0 || x - vxw >= tsize || y - vyw <= 0 || y - vyw >= tsize);
boolean outside = false;
if (rounded && !outside) {
v[2] = (short) ((uxw - vxw) * S1000);
v[3] = (short) ((uyw - vyw) * S1000);
v[4] = -1;
v[5] = 1;
si = addVertex(v, si);
si = addVertex(v, si);
// Add the first point twice to be able to draw with GL_TRIANGLE_STRIP
curVertices[vertexPos++] = (short) ((x + uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 1;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x + uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 1;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 1;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
v[2] = (short) (-(uxw + vxw) * S1000);
v[3] = (short) (-(uyw + vyw) * S1000);
v[4] = 1;
v[5] = 1;
si = addVertex(v, si);
// Start of line
curVertices[vertexPos++] = (short) ((x + uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 0;
v[2] = (short) ((uxw) * S1000);
v[3] = (short) ((uyw) * S1000);
v[4] = -1;
v[5] = 0;
si = addVertex(v, si);
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 0;
v[2] = (short) ((-uxw) * S1000);
v[3] = (short) ((-uyw) * S1000);
v[4] = 1;
v[5] = 0;
si = addVertex(v, si);
} else {
// outside means line is probably clipped
// TODO should align ending with tile boundary
// for now, just extend the line a little
if (!outside) {
if (squared) {
vxw = 0;
vyw = 0;
} else if (!outside) {
vxw *= 0.5;
vyw *= 0.5;
}
if (rounded) {
if (rounded)
verticesCnt -= 2;
}
// Add the first point twice to be able to draw with GL_TRIANGLE_STRIP
curVertices[vertexPos++] = (short) ((x + uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 0;
v[2] = (short) ((uxw - vxw) * S1000);
v[3] = (short) ((uyw - vyw) * S1000);
v[4] = -1;
v[5] = 0;
si = addVertex(v, si);
si = addVertex(v, si);
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x + uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 0;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 0;
v[2] = (short) (-(uxw + vxw) * S1000);
v[3] = (short) (-(uyw + vyw) * S1000);
v[4] = 1;
v[5] = 0;
si = addVertex(v, si);
}
prevX = x;
prevY = y;
x = nextX;
y = nextY;
// boolean flipped = false;
for (; pointPos < pos + length;) {
nextX = pointArray[pointPos++];
@ -235,58 +243,40 @@ class LineLayer {
// Sum of these two vectors points
ux = vx + wx;
uy = vy + wy;
a = -wy * ux + wx * uy;
if ((a < 0.1 && a > -0.1)) {
// Almost straight, use normal vector
// boolean split = false;
if (a < 0.1f && a > -0.1f) {
// Almost straight or miter goes to infinity, use normal vector
ux = -wy;
uy = wx;
} else {
ux = (ux / a);
uy = (uy / a);
if (ux > 2 || uy > 2 || ux < -2 || uy < -2) {
if (ux > 2.0f || ux < -2.0f || uy > 2.0f || uy < -2.0f) {
ux = -wy;
uy = wx;
// ux = vx + wx;
// uy = vy + wy;
// // Normalize u, and project normal vector onto this
// double c = Math.sqrt(ux * ux + uy * uy);
// if (a < 0) {
// ux = (float) -(ux / c);
// uy = (float) -(uy / c);
// }
// else {
// ux = (float) (ux / c);
// uy = (float) (uy / c);
// }
// flipped = flipped ? false : true;
}
}
uxw = ux * w;
uyw = uy * w;
uxw = ux * S1000;
uyw = uy * S1000;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
v[6] = v[0] = (short) (x * S);
v[7] = v[1] = (short) (y * S);
curVertices[vertexPos++] = (short) ((x + uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 0;
v[2] = (short) uxw;
v[3] = (short) uyw;
v[4] = -1;
v[5] = 0;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 0;
v[8] = (short) -uxw;
v[9] = (short) -uyw;
v[10] = 1;
v[11] = 0;
si = addTwoVertex(v, si);
prevX = x;
prevY = y;
@ -305,105 +295,71 @@ class LineLayer {
ux = vy;
uy = -vx;
uxw = ux * w;
uyw = uy * w;
uxw = ux;
uyw = uy;
vxw = vx * w;
vyw = vy * w;
vxw = vx;
vyw = vy;
// outside = (x <= 0 || x >= Tile.TILE_SIZE || y <= 0 || y >= Tile.TILE_SIZE)
// && (x - vxw <= 0 || x - vxw >= Tile.TILE_SIZE || y - vyw <= 0 || y - vyw >= Tile.TILE_SIZE);
outside = (x <= 0 || x >= tsize || y <= 0 || y >= tsize)
&& (x - vxw <= 0 || x - vxw >= tsize || y - vyw <= 0 || y - vyw >= tsize);
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
v[0] = (short) (x * S);
v[1] = (short) (y * S);
if (rounded && !outside) {
curVertices[vertexPos++] = (short) ((x + uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 0;
v[2] = (short) ((uxw) * S1000);
v[3] = (short) ((uyw) * S1000);
v[4] = -1;
v[5] = 0;
si = addVertex(v, si);
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 0;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
v[2] = (short) ((-uxw) * S1000);
v[3] = (short) ((-uyw) * S1000);
v[4] = 1;
v[5] = 0;
si = addVertex(v, si);
// For rounded line edges
curVertices[vertexPos++] = (short) ((x + uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = -1;
v[2] = (short) ((uxw - vxw) * S1000);
v[3] = (short) ((uyw - vyw) * S1000);
v[4] = -1;
v[5] = -1;
si = addVertex(v, si);
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
// Add the last vertex twice to be able to draw with GL_TRIANGLE_STRIP
curVertices[vertexPos++] = (short) ((x - uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = -1;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = -1;
v[2] = (short) (-(uxw + vxw) * S1000);
v[3] = (short) (-(uyw + vyw) * S1000);
v[4] = 1;
v[5] = -1;
si = addVertex(v, si);
si = addVertex(v, si);
} else {
if (!outside) {
if (squared) {
vxw = 0;
vyw = 0;
} else if (!outside) {
vxw *= 0.5;
vyw *= 0.5;
}
if (rounded) {
if (rounded)
verticesCnt -= 2;
}
curVertices[vertexPos++] = (short) ((x + uxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y + uyw) * SCALE_FACTOR);
curVertices[vertexPos++] = -1;
curVertices[vertexPos++] = 0;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
// Add the last vertex twice to be able to draw with GL_TRIANGLE_STRIP
curVertices[vertexPos++] = (short) ((x - uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 0;
if (vertexPos == MAX) {
curVertices = getNextItem();
vertexPos = 0;
}
curVertices[vertexPos++] = (short) ((x - uxw - vxw) * SCALE_FACTOR);
curVertices[vertexPos++] = (short) ((y - uyw - vyw) * SCALE_FACTOR);
curVertices[vertexPos++] = 1;
curVertices[vertexPos++] = 0;
v[2] = (short) ((uxw) * S1000);
v[3] = (short) ((uyw) * S1000);
v[4] = -1;
v[5] = 0;
si = addVertex(v, si);
v[2] = (short) (-(uxw + vxw) * S1000);
v[3] = (short) (-(uyw + vyw) * S1000);
v[4] = 1;
v[5] = 0;
si = addVertex(v, si);
si = addVertex(v, si);
}
curItem.used = vertexPos;
curItem = si;
}
}

View File

@ -14,136 +14,167 @@
*/
package org.mapsforge.android.glrenderer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
import static android.opengl.GLES20.glDisableVertexAttribArray;
import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniform4f;
import static android.opengl.GLES20.glUniform4fv;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import java.nio.ShortBuffer;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.android.utils.GlUtils;
import android.opengl.GLES20;
import android.util.FloatMath;
class LineLayers {
private static int NUM_VERTEX_FLOATS = 4;
private static int NUM_VERTEX_SHORTS = 6;
// static FloatBuffer compileLayerData(LineLayer layers, FloatBuffer buf) {
// FloatBuffer fbuf = buf;
// int size = 0;
//
// for (LineLayer l = layers; l != null; l = l.next)
// size += l.verticesCnt;
//
// size *= NUM_VERTEX_FLOATS;
//
// if (buf == null || buf.capacity() < size) {
// ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 4).order(
// ByteOrder.nativeOrder());
// fbuf = bbuf.asFloatBuffer();
// } else {
// fbuf.clear();
// }
// int pos = 0;
//
// PoolItem last = null, items = null;
//
// for (LineLayer l = layers; l != null; l = l.next) {
// if (l.isOutline)
// continue;
//
// for (PoolItem item = l.pool; item != null; item = item.next) {
// fbuf.put(item.vertices, 0, item.used);
// last = item;
// }
// l.offset = pos;
// pos += l.verticesCnt;
//
// if (last != null) {
// last.next = items;
// items = l.pool;
// }
//
// l.pool = null;
// }
//
// VertexPool.add(items);
//
// fbuf.flip();
//
// return fbuf;
// }
//
// static ShortBuffer compileLayerData(LineLayer layers, ShortBuffer buf) {
// int size = 0;
// ShortBuffer sbuf = buf;
//
// for (LineLayer l = layers; l != null; l = l.next)
// size += l.verticesCnt;
//
// size *= NUM_VERTEX_FLOATS;
//
// if (buf == null || buf.capacity() < size) {
// ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 2).order(
// ByteOrder.nativeOrder());
// sbuf = bbuf.asShortBuffer();
// } else {
// sbuf.clear();
// }
// int pos = 0;
//
// short[] data = new short[PoolItem.SIZE];
//
// PoolItem last = null, items = null;
//
// for (LineLayer l = layers; l != null; l = l.next) {
// if (l.isOutline)
// continue;
//
// for (PoolItem item = l.pool; item != null; item = item.next) {
// PoolItem.toHalfFloat(item, data);
// sbuf.put(data, 0, item.used);
// last = item;
// }
//
// l.offset = pos;
// pos += l.verticesCnt;
//
// if (last != null) {
// last.next = items;
// items = l.pool;
// }
//
// l.pool = null;
// }
//
// VertexPool.add(items);
//
// sbuf.flip();
//
// return sbuf;
// }
//
// static void clear(LineLayer layer) {
// for (LineLayer l = layer; l != null; l = l.next) {
// if (l.pool != null)
// VertexPool.add(l.pool);
// }
// }
private static final int LINE_VERTICES_DATA_POS_OFFSET = 0;
private static final int LINE_VERTICES_DATA_TEX_OFFSET = 8;
static ShortBuffer compileLayerData(LineLayer layers, ShortBuffer buf) {
// shader handles
private static int lineProgram;
private static int hLineVertexPosition;
private static int hLineTexturePosition;
private static int hLineColor;
private static int hLineMatrix;
private static int hLineScale;
private static int hLineWidth;
static boolean init() {
lineProgram = GlUtils.createProgram(Shaders.lineVertexShader,
Shaders.lineFragmentShader);
if (lineProgram == 0) {
// Log.e(TAG, "Could not create line program.");
return false;
}
hLineMatrix = glGetUniformLocation(lineProgram, "mvp");
hLineScale = glGetUniformLocation(lineProgram, "u_wscale");
hLineWidth = glGetUniformLocation(lineProgram, "u_width");
hLineColor = glGetUniformLocation(lineProgram, "u_color");
hLineVertexPosition = GLES20.glGetAttribLocation(lineProgram, "a_position");
hLineTexturePosition = glGetAttribLocation(lineProgram, "a_st");
return true;
}
static LineLayer drawLines(GLMapTile tile, LineLayer layer, int next, float[] matrix,
float div, double zoom, float scale) {
float z = 1 / div;
if (layer == null)
return null;
glUseProgram(lineProgram);
glEnableVertexAttribArray(hLineVertexPosition);
glEnableVertexAttribArray(hLineTexturePosition);
glVertexAttribPointer(hLineVertexPosition, 4, GLES20.GL_SHORT,
false, 12, tile.lineOffset + LINE_VERTICES_DATA_POS_OFFSET);
glVertexAttribPointer(hLineTexturePosition, 2, GLES20.GL_SHORT,
false, 12, tile.lineOffset + LINE_VERTICES_DATA_TEX_OFFSET);
glUniformMatrix4fv(hLineMatrix, 1, false, matrix, 0);
// if (diff != 0)
// // diff < 0 means tile is parent
// z = (diff > 0) ? 1.0f / (1 << diff) : (1 << -diff);
// scale factor to map one pixel on tile to one pixel on screen:
// float pixel = 2.0f / (scale * z);
// GLES20.glUniform1f(hLineScale, pixel);
// line scale factor (for non fixed lines)
float s = FloatMath.sqrt(scale * z);
boolean blur = false;
GLES20.glUniform1f(hLineScale, 0);
LineLayer l = layer;
for (; l != null && l.layer < next; l = l.next) {
Line line = l.line;
if (line.fade != -1 && line.fade > zoom)
continue;
if (line.fade >= zoom) {
float alpha = 1.0f;
alpha = (scale > 1.2f ? scale : 1.2f) - alpha;
if (alpha > 1.0f)
alpha = 1.0f;
glUniform4f(hLineColor,
line.color[0], line.color[1], line.color[2], alpha);
} else {
glUniform4fv(hLineColor, 1, line.color, 0);
}
if (blur) {
GLES20.glUniform1f(hLineScale, 0);
blur = false;
}
if (l.isOutline) {
for (LineLayer o = l.outlines; o != null; o = o.outlines) {
if (line.blur != 0) {
GLES20.glUniform1f(hLineScale, (l.width + o.width) / (scale * z)
- (line.blur / (scale * z)));
blur = true;
}
if (zoom > MapGenerator.STROKE_MAX_ZOOM_LEVEL)
GLES20.glUniform1f(hLineWidth,
(l.width + o.width) / (scale * z));
else
GLES20.glUniform1f(hLineWidth, l.width / (scale * z)
+ o.width / s);
glDrawArrays(GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
}
}
else {
if (line.blur != 0) {
GLES20.glUniform1f(hLineScale, (l.width / s) * line.blur);
blur = true;
}
if (line.fixed || zoom > MapGenerator.STROKE_MAX_ZOOM_LEVEL) {
// invert scaling of extrusion vectors so that line width stays the same
GLES20.glUniform1f(hLineWidth, (l.width / (scale * z)));
} else {
GLES20.glUniform1f(hLineWidth, (l.width / s));
}
glDrawArrays(GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);
}
}
glDisableVertexAttribArray(hLineVertexPosition);
glDisableVertexAttribArray(hLineTexturePosition);
return l;
}
static int sizeOf(LineLayer layers) {
int size = 0;
ShortBuffer sbuf = buf;
for (LineLayer l = layers; l != null; l = l.next)
size += l.verticesCnt;
size *= NUM_VERTEX_FLOATS;
size *= NUM_VERTEX_SHORTS;
return size;
}
if (buf == null || buf.capacity() < size) {
ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 2).order(
ByteOrder.nativeOrder());
sbuf = bbuf.asShortBuffer();
} else {
sbuf.clear();
}
static void compileLayerData(LineLayer layers, ShortBuffer sbuf) {
int pos = 0;
// short[] data = new short[PoolItem.SIZE];
ShortItem last = null, items = null;
for (LineLayer l = layers; l != null; l = l.next) {
@ -151,8 +182,6 @@ class LineLayers {
continue;
for (ShortItem item = l.pool; item != null; item = item.next) {
// PoolItem.toHalfFloat(item, data);
// sbuf.put(data, 0, item.used);
sbuf.put(item.vertices, 0, item.used);
last = item;
}
@ -166,19 +195,110 @@ class LineLayers {
}
l.pool = null;
l.curItem = null;
}
ShortPool.add(items);
sbuf.flip();
return sbuf;
}
// @SuppressLint("UseValueOf")
// private static final Boolean lock = new Boolean(true);
// private static final int POOL_LIMIT = 1500;
//
// static private LineLayer pool = null;
// static private int count = 0;
// static private int countAll = 0;
//
// static void finish() {
// synchronized (lock) {
// count = 0;
// countAll = 0;
// pool = null;
// }
// }
//
// static LineLayer get(int layer, Line line, float width, boolean outline) {
// synchronized (lock) {
//
// if (count == 0 && pool == null) {
// countAll++;
// return new LineLayer(layer, line, width, outline);
// }
// if (count > 0) {
// count--;
// } else {
// int c = 0;
// LineLayer tmp = pool;
//
// while (tmp != null) {
// c++;
// tmp = tmp.next;
// }
//
// Log.d("LineLayersl", "eek wrong count: " + c + " left");
// }
//
// LineLayer it = pool;
// pool = pool.next;
// it.next = null;
// it.layer = layer;
// it.line = line;
// it.isOutline = outline;
// it.width = width;
// return it;
// }
// }
//
// static void add(LineLayer layers) {
// if (layers == null)
// return;
//
// synchronized (lock) {
//
// // limit pool items
// if (countAll < POOL_LIMIT) {
// LineLayer last = layers;
//
// while (true) {
// count++;
//
// if (last.next == null)
// break;
//
// last = last.next;
// }
//
// last.next = pool;
// pool = layers;
//
// } else {
// int cleared = 0;
// LineLayer prev, tmp = layers;
// while (tmp != null) {
// prev = tmp;
// tmp = tmp.next;
//
// countAll--;
// cleared++;
//
// prev.next = null;
//
// }
// Log.d("LineLayers", "sum: " + countAll + " free: " + count + " freed "
// + cleared);
// }
//
// }
// }
//
static void clear(LineLayer layer) {
for (LineLayer l = layer; l != null; l = l.next) {
if (l.pool != null)
if (l.pool != null) {
ShortPool.add(l.pool);
l.pool = null;
l.curItem = null;
}
}
// LineLayers.add(layer);
}
}

View File

@ -14,8 +14,6 @@
*/
package org.mapsforge.android.glrenderer;
import java.util.ArrayList;
import org.mapsforge.android.mapgenerator.IMapGenerator;
import org.mapsforge.android.mapgenerator.MapGeneratorJob;
import org.mapsforge.android.rendertheme.IRenderCallback;
@ -23,6 +21,7 @@ import org.mapsforge.android.rendertheme.RenderTheme;
import org.mapsforge.android.rendertheme.renderinstruction.Area;
import org.mapsforge.android.rendertheme.renderinstruction.Caption;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.android.rendertheme.renderinstruction.PathText;
import org.mapsforge.android.rendertheme.renderinstruction.RenderInstruction;
import org.mapsforge.core.MercatorProjection;
import org.mapsforge.core.Tag;
@ -46,11 +45,11 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
private static final double PI180 = (Math.PI / 180) / 1000000.0;
private static final double PIx4 = Math.PI * 4;
private static final double STROKE_INCREASE = Math.sqrt(2);
private static final byte STROKE_MIN_ZOOM_LEVEL = 12;
private static final byte LAYERS = 11;
private static final double f900913 = 20037508.342789244;
// 134217728
// 2147483648.000
static final byte STROKE_MIN_ZOOM_LEVEL = 12;
static final byte STROKE_MAX_ZOOM_LEVEL = 17;
private static RenderTheme renderTheme;
@ -66,14 +65,23 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
private LineLayer mCurLineLayer;
private PolygonLayer mCurPolyLayer;
private ArrayList<TextItem> mLabels;
private TextItem mLabels;
private int mDrawingLayer;
private int mLevels;
private boolean useSphericalMercator = false;
private float mStrokeScale = 1.0f;
private boolean mProjected;
// private boolean mProjectedResult;
private float mSimplify;
// private boolean firstMatch;
// private boolean prevClosed;
private RenderInstruction[] mRenderInstructions = null;
private final String TAG_WATER = "water".intern();
/**
*
*/
@ -84,53 +92,62 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
private float mPoiX = 256;
private float mPoiY = 256;
private Tag mTagEmptyName = new Tag("name", "");
private Tag mTagEmptyName = new Tag(Tag.TAG_KEY_NAME, null, false);
private Tag mTagName;
private void filterTags(Tag[] tags) {
for (int i = 0; i < tags.length; i++) {
// Log.d(TAG, "check tag: " + tags[i]);
if (tags[i].key == mTagEmptyName.key && tags[i].value != null) {
if (tags[i].key == Tag.TAG_KEY_NAME && tags[i].value != null) {
mTagName = tags[i];
tags[i] = mTagEmptyName;
}
}
}
// private RenderInstruction[] mNodeRenderInstructions;
@Override
public void renderPointOfInterest(byte layer, float latitude, float longitude,
Tag[] tags) {
mTagName = null;
long x = mCurrentTile.x;
long y = mCurrentTile.y;
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
if (mMapProjection != null)
{
long x = mCurrentTile.x;
long y = mCurrentTile.y;
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
double divx, divy;
long dx = (x - (z >> 1));
long dy = (y - (z >> 1));
double divx, divy;
long dx = (x - (z >> 1));
long dy = (y - (z >> 1));
if (useSphericalMercator) {
divx = f900913 / (z >> 1);
divy = f900913 / (z >> 1);
mPoiX = (float) (longitude / divx - dx);
mPoiY = (float) (latitude / divy + dy);
if (mMapProjection == WebMercator.NAME) {
double div = f900913 / (z >> 1);
// divy = f900913 / (z >> 1);
mPoiX = (float) (longitude / div - dx);
mPoiY = (float) (latitude / div + dy);
} else {
divx = 180000000.0 / (z >> 1);
divy = z / PIx4;
mPoiX = (float) (longitude / divx - dx);
double sinLat = Math.sin(latitude * PI180);
mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10
|| mPoiY > Tile.TILE_SIZE + 10)
return;
}
} else {
divx = 180000000.0 / (z >> 1);
divy = z / PIx4;
mPoiX = (float) (longitude / divx - dx);
double sinLat = Math.sin(latitude * PI180);
mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10
|| mPoiY > Tile.TILE_SIZE + 10)
return;
mPoiX = longitude;
mPoiY = latitude;
}
// remove tags that should not be cached in Rendertheme
filterTags(tags);
// Log.d(TAG, "renderPointOfInterest: " + mTagName);
// mNodeRenderInstructions =
MapGenerator.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel);
}
@ -140,82 +157,6 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
}
private boolean mProjected;
private boolean mProjectedResult;
private float mSimplify;
private boolean projectToTile() {
if (mProjected)
return mProjectedResult;
float[] coords = mWayNodes;
long x = mCurrentTile.x;
long y = mCurrentTile.y;
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
float min = mSimplify;
double divx, divy;
long dx = (x - (z >> 1));
long dy = (y - (z >> 1));
if (useSphericalMercator) {
divx = f900913 / (z >> 1);
divy = f900913 / (z >> 1);
} else {
divx = 180000000.0 / (z >> 1);
divy = z / PIx4;
}
for (int pos = 0, outPos = 0, i = 0, m = mWays.length; i < m; i++) {
int len = mWays[i];
if (len == 0)
continue;
int cnt = 0;
float lat, lon, prevLon = 0, prevLat = 0;
for (int end = pos + len; pos < end; pos += 2) {
if (useSphericalMercator) {
lon = (float) (coords[pos] / divx - dx);
lat = (float) (coords[pos + 1] / divy + dy);
} else {
lon = (float) ((coords[pos]) / divx - dx);
double sinLat = Math.sin(coords[pos + 1] * PI180);
lat = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
}
if (cnt != 0) {
// drop small distance intermediate nodes
if (lat == prevLat && lon == prevLon)
continue;
if ((pos != end - 2) &&
!((lat > prevLat + min || lat < prevLat - min) ||
(lon > prevLon + min || lon < prevLon - min)))
continue;
}
coords[outPos++] = prevLon = lon;
coords[outPos++] = prevLat = lat;
cnt += 2;
}
mWays[i] = (short) cnt;
}
mProjected = true;
mProjectedResult = true;
return true;
}
// private boolean firstMatch;
// private boolean prevClosed;
private RenderInstruction[] mRenderInstructions = null;
private final String TAG_WATER = "water".intern();
@Override
public void renderWay(byte layer, Tag[] tags, float[] wayNodes, short[] wayLength,
boolean closed) {
@ -239,6 +180,9 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
mWayNodes = wayNodes;
mWays = wayLength;
// remove tags that should not be cached in Rendertheme
filterTags(tags);
// if (mRenderInstructions != null) {
// for (int i = 0, n = mRenderInstructions.length; i < n; i++)
// mRenderInstructions[i].renderWay(this, tags);
@ -266,7 +210,6 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagWay,
(byte) 0, true, true);
}
}
@Override
@ -277,9 +220,10 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
return;
if (caption.textKey == mTagEmptyName.key) {
if (mLabels == null)
mLabels = new ArrayList<TextItem>();
mLabels.add(new TextItem(mWayNodes[0], mWayNodes[1], mTagName.value, caption));
TextItem t = new TextItem(mWayNodes[0], mWayNodes[1], mTagName.value, caption);
t.next = mLabels;
mLabels = t;
}
}
@ -292,9 +236,23 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
return;
if (caption.textKey == mTagEmptyName.key) {
if (mLabels == null)
mLabels = new ArrayList<TextItem>();
mLabels.add(new TextItem(mPoiX, mPoiY, mTagName.value, caption));
TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, caption);
t.next = mLabels;
mLabels = t;
}
}
@Override
public void renderWayText(PathText pathText) {
// Log.d(TAG, "renderWayText: " + mTagName);
if (mTagName == null)
return;
if (pathText.textKey == mTagEmptyName.key) {
mLabels = WayDecorator.renderText(mWayNodes, mTagName.value, pathText, 0,
mWays[0], mLabels);
}
}
@ -320,13 +278,23 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
private int countNodes;
@Override
public void renderWay(Line line) {
public void renderWay(Line line, int level) {
projectToTile();
LineLayer outlineLayer = null;
if (line.outline && mCurLineLayer == null)
return;
float w = line.width;
if (!line.fixed) {
w *= mStrokeScale;
w *= mProjectionScaleFactor;
}
LineLayer lineLayer = null;
int numLayer = mDrawingLayer + line.level;
int numLayer = mDrawingLayer + level;
LineLayer l = mLineLayers;
@ -334,7 +302,9 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
lineLayer = mCurLineLayer;
} else if (l == null || l.layer > numLayer) {
// insert new layer at start
lineLayer = new LineLayer(numLayer, line, false);
lineLayer = new LineLayer(numLayer, line, w, line.outline);
// lineLayer = LineLayers.get(numLayer, line, w, false);
lineLayer.next = l;
mLineLayers = lineLayer;
} else {
@ -346,7 +316,8 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
}
// insert new layer between current and next layer
if (l.next == null || l.next.layer > numLayer) {
lineLayer = new LineLayer(numLayer, line, false);
lineLayer = new LineLayer(numLayer, line, w, line.outline);
// lineLayer = LineLayers.get(numLayer, line, w, false);
lineLayer.next = l.next;
l.next = lineLayer;
}
@ -357,77 +328,83 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
if (lineLayer == null)
return;
if (line.outline) {
lineLayer.addOutline(mCurLineLayer);
return;
}
mCurLineLayer = lineLayer;
float w = line.strokeWidth;
if (!line.fixed) {
w *= mStrokeScale;
w *= mProjectionScaleFactor;
}
else {
w *= 1.2; // TODO make this dependent on dpi
}
boolean round = line.round;
for (int i = 0, pos = 0, n = mWays.length; i < n; i++) {
int length = mWays[i];
if (length < 0)
break;
// save some vertices
if (round && i > 200) {
// Log.d(TAG, "WAY TOO MANY LINES!!!");
round = false;
}
// need at least two points
if (length >= 4) {
lineLayer.addLine(mWayNodes, pos, length, w, line.round);
lineLayer.addLine(mWayNodes, pos, length);
countLines++;
countNodes += length;
}
pos += length;
}
if (line.outline < 0)
return;
Line outline = MapGenerator.renderTheme.getOutline(line.outline);
if (outline == null)
return;
numLayer = mDrawingLayer + outline.level;
l = mLineLayers;
if (l == null || l.layer > numLayer) {
// insert new layer at start
outlineLayer = new LineLayer(numLayer, outline, true);
outlineLayer.next = l;
mLineLayers = outlineLayer;
} else {
while (l != null) {
if (l.layer == numLayer) {
outlineLayer = l;
break;
}
// insert new layer between current and next layer
if (l.next == null || l.next.layer > numLayer) {
outlineLayer = new LineLayer(numLayer, outline, true);
outlineLayer.next = l.next;
l.next = outlineLayer;
}
l = l.next;
}
}
if (outlineLayer != null)
outlineLayer.addOutline(lineLayer);
// if (line.outline < 0)
// return;
//
// Line outline = MapGenerator.renderTheme.getOutline(line.outline);
//
// if (outline == null)
// return;
//
// numLayer = mDrawingLayer + outline.getLevel();
//
// l = mLineLayers;
//
// if (l == null || l.layer > numLayer) {
// // insert new layer at start
// outlineLayer = new LineLayer(numLayer, outline, w, true);
// // outlineLayer = LineLayers.get(numLayer, outline, w, true);
// outlineLayer.next = l;
// mLineLayers = outlineLayer;
// } else {
// while (l != null) {
// if (l.layer == numLayer) {
// outlineLayer = l;
// break;
// }
// // insert new layer between current and next layer
// if (l.next == null || l.next.layer > numLayer) {
// outlineLayer = new LineLayer(numLayer, outline, w, true);
// // outlineLayer = LineLayers.get(numLayer, outline, w, true);
// outlineLayer.next = l.next;
// l.next = outlineLayer;
// }
// l = l.next;
// }
// }
//
// if (outlineLayer != null)
// outlineLayer.addOutline(lineLayer);
}
@Override
public void renderArea(Area area) {
public void renderArea(Area area, int level) {
if (!mDebugDrawPolygons)
return;
if (!projectToTile())
if (!mProjected && !projectToTile())
return;
int numLayer = mDrawingLayer + area.level;
int numLayer = mDrawingLayer + level;
PolygonLayer layer = null;
PolygonLayer l = mPolyLayers;
@ -463,6 +440,9 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
for (int i = 0, pos = 0, n = mWays.length; i < n; i++) {
int length = mWays[i];
if (length < 0)
break;
// need at least three points
if (length >= 6)
layer.addPolygon(mWayNodes, pos, length);
@ -470,6 +450,7 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
pos += length;
}
// if (area.line != null)
}
@Override
@ -478,12 +459,6 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
}
@Override
public void renderWayText(String text, Paint paint, Paint stroke) {
// TODO Auto-generated method stub
}
@Override
public void cleanup() {
// TODO Auto-generated method stub
@ -495,23 +470,27 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
@Override
public boolean executeJob(MapGeneratorJob mapGeneratorJob) {
GLMapTile tile;
if (mMapDatabase == null)
return false;
useSphericalMercator = WebMercator.NAME.equals(mMapDatabase.getMapProjection());
mCurrentTile = (GLMapTile) mapGeneratorJob.tile;
tile = mCurrentTile = (GLMapTile) mapGeneratorJob.tile;
mDebugDrawPolygons = !mapGeneratorJob.debugSettings.mDisablePolygons;
mDebugDrawUnmatched = mapGeneratorJob.debugSettings.mDrawUnmatchted;
if (mCurrentTile.isLoading || mCurrentTile.isReady)
if (tile.isLoading || tile.isReady || tile.isCanceled)
return false;
mCurrentTile.isLoading = true;
tile.isLoading = true;
mLevels = MapGenerator.renderTheme.getLevels();
setScaleStrokeWidth(mCurrentTile.zoomLevel);
// limit stroke scale at z=17
if (tile.zoomLevel < STROKE_MAX_ZOOM_LEVEL)
setScaleStrokeWidth(tile.zoomLevel);
else
setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL);
mLineLayers = null;
mPolyLayers = null;
@ -520,43 +499,54 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
// firstMatch = true;
countLines = 0;
countNodes = 0;
mProjectionScaleFactor = (float) (1.0 / Math.cos(MercatorProjection
.pixelYToLatitude(mCurrentTile.pixelY, mCurrentTile.zoomLevel)
* (Math.PI / 180))); // / 1.5f;
if (mMapDatabase.executeQuery(mCurrentTile, this) != QueryResult.SUCCESS) {
// acount for area changes with latitude
mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection
.pixelYToLatitude(tile.pixelY, tile.zoomLevel)
* (Math.PI / 180)));
if (mMapDatabase.executeQuery(tile, this) != QueryResult.SUCCESS) {
Log.d(TAG, "Failed loading: " + tile);
LineLayers.clear(mLineLayers);
PolygonLayers.clear(mPolyLayers);
mLineLayers = null;
mPolyLayers = null;
mCurrentTile.isLoading = false;
tile.isLoading = false;
return false;
}
if (mapGeneratorJob.debugSettings.mDrawTileFrames) {
final float[] debugBoxCoords = { 0, 0, 0, Tile.TILE_SIZE,
Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0, 0, 0 };
final short[] debugBoxIndex = { 10 };
mTagName = new Tag("name", countLines + " " + countNodes + " "
+ mCurrentTile.toString(), false);
mPoiX = 10;
+ tile.toString(), false);
mPoiX = Tile.TILE_SIZE >> 1;
mPoiY = 10;
MapGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
// float[] coords = { 0, 0, 0, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE,
// Tile.TILE_SIZE, 0, 0, 0 };
// LineLayer ll = mLineLayers.getLayer(Integer.MAX_VALUE, Color.BLACK, false,
// true, -1);
// ll.addLine(coords, 0, coords.length, 1.5f, false);
mWays = debugBoxIndex;
mWayNodes = debugBoxCoords;
mDrawingLayer = 10 * mLevels;
MapGenerator.renderTheme.matchWay(this, debugTagBox, (byte) 0, false, true);
}
mCurrentTile.lineLayers = mLineLayers;
mCurrentTile.polygonLayers = mPolyLayers;
mCurrentTile.labels = mLabels;
tile.lineLayers = mLineLayers;
tile.polygonLayers = mPolyLayers;
tile.labels = mLabels;
mCurPolyLayer = null;
mCurLineLayer = null;
mCurrentTile.newData = true;
tile.newData = true;
tile.isLoading = false;
return true;
}
private Tag[] debugTagWay = { new Tag("debug", "way") };
private Tag[] debugTagArea = { new Tag("debug", "area") };
private final Tag[] debugTagBox = { new Tag("debug", "box") };
private final Tag[] debugTagWay = { new Tag("debug", "way") };
private final Tag[] debugTagArea = { new Tag("debug", "area") };
private float mProjectionScaleFactor;
@ -586,9 +576,12 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
mStrokeScale = 1;
}
private String mMapProjection;
@Override
public void setMapDatabase(IMapDatabase mapDatabase) {
mMapDatabase = mapDatabase;
mMapProjection = mMapDatabase.getMapProjection();
}
@Override
@ -609,4 +602,76 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas
return mRenderInstructions != null;
}
private boolean projectToTile() {
if (mProjected || mMapProjection == null)
return true;
boolean useWebMercator = false;
if (mMapProjection == WebMercator.NAME)
useWebMercator = true;
float[] coords = mWayNodes;
long x = mCurrentTile.x;
long y = mCurrentTile.y;
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
float min = mSimplify;
double divx, divy;
long dx = (x - (z >> 1));
long dy = (y - (z >> 1));
if (useWebMercator) {
divx = f900913 / (z >> 1);
divy = f900913 / (z >> 1);
} else {
divx = 180000000.0 / (z >> 1);
divy = z / PIx4;
}
for (int pos = 0, outPos = 0, i = 0, m = mWays.length; i < m; i++) {
int len = mWays[i];
if (len == 0)
continue;
if (len < 0)
break;
int cnt = 0;
float lat, lon, prevLon = 0, prevLat = 0;
for (int end = pos + len; pos < end; pos += 2) {
if (useWebMercator) {
lon = (float) (coords[pos] / divx - dx);
lat = (float) (coords[pos + 1] / divy + dy);
} else {
lon = (float) ((coords[pos]) / divx - dx);
double sinLat = Math.sin(coords[pos + 1] * PI180);
lat = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
}
if (cnt != 0) {
// drop small distance intermediate nodes
if (lat == prevLat && lon == prevLon)
continue;
if ((pos != end - 2) &&
!((lat > prevLat + min || lat < prevLat - min) ||
(lon > prevLon + min || lon < prevLon - min)))
continue;
}
coords[outPos++] = prevLon = lon;
coords[outPos++] = prevLat = lat;
cnt += 2;
}
mWays[i] = (short) cnt;
}
mProjected = true;
// mProjectedResult = true;
return true;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -15,11 +15,14 @@
package org.mapsforge.android.glrenderer;
import org.mapsforge.android.rendertheme.renderinstruction.Area;
import org.mapsforge.core.Tile;
class PolygonLayer {
private static final float S = MapRenderer.COORD_MULTIPLIER;
PolygonLayer next;
Area area;
private static final float SCALE_FACTOR = 16.0f;
// private static final float MapRenderer.COORD_MULTIPLIER = 8.0f;
private boolean first = true;
private float originX;
private float originY;
@ -53,8 +56,8 @@ class PolygonLayer {
if (first) {
first = false;
originX = points[pos];
originY = points[pos + 1];
originX = Tile.TILE_SIZE >> 1; // points[pos];
originY = Tile.TILE_SIZE >> 1; // points[pos + 1];
}
short[] curVertices = curItem.vertices;
@ -65,24 +68,25 @@ class PolygonLayer {
outPos = 0;
}
curVertices[outPos++] = (short) (originX * SCALE_FACTOR);
curVertices[outPos++] = (short) (originY * SCALE_FACTOR);
curVertices[outPos++] = (short) (originX * S);
curVertices[outPos++] = (short) (originY * S);
int MAX = ShortItem.SIZE;
int remaining = length;
int inPos = pos;
while (remaining > 0) {
if (outPos == ShortItem.SIZE) {
if (outPos == MAX) {
curVertices = getNextItem();
outPos = 0;
}
int len = remaining;
if (len > (ShortItem.SIZE) - outPos)
len = (ShortItem.SIZE) - outPos;
if (len > MAX - outPos)
len = MAX - outPos;
for (int i = 0; i < len; i++)
curVertices[outPos++] = (short) (points[inPos++] * SCALE_FACTOR);
curVertices[outPos++] = (short) (points[inPos++] * S);
// System.arraycopy(points, inPos, curVertices, outPos, len);
@ -92,13 +96,13 @@ class PolygonLayer {
remaining -= len;
}
if (outPos == PoolItem.SIZE) {
if (outPos == MAX) {
curVertices = getNextItem();
outPos = 0;
}
curVertices[outPos++] = (short) (points[pos + 0] * SCALE_FACTOR);
curVertices[outPos++] = (short) (points[pos + 1] * SCALE_FACTOR);
curVertices[outPos++] = (short) (points[pos + 0] * S);
curVertices[outPos++] = (short) (points[pos + 1] * S);
curItem.used = outPos;
}

View File

@ -14,172 +14,269 @@
*/
package org.mapsforge.android.glrenderer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_EQUAL;
import static android.opengl.GLES20.GL_INVERT;
import static android.opengl.GLES20.GL_KEEP;
import static android.opengl.GLES20.GL_NEVER;
import static android.opengl.GLES20.GL_STENCIL_TEST;
import static android.opengl.GLES20.GL_TRIANGLE_FAN;
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
import static android.opengl.GLES20.GL_ZERO;
import static android.opengl.GLES20.glColorMask;
import static android.opengl.GLES20.glDisable;
import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glStencilFunc;
import static android.opengl.GLES20.glStencilMask;
import static android.opengl.GLES20.glStencilOp;
import static android.opengl.GLES20.glUniform4f;
import static android.opengl.GLES20.glUniform4fv;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import java.nio.ShortBuffer;
import org.mapsforge.core.Tile;
import org.mapsforge.android.utils.GlUtils;
import android.opengl.GLES20;
class PolygonLayers {
private static final int NUM_VERTEX_FLOATS = 2;
// static final float[] mFillCoords = { -2, Tile.TILE_SIZE + 1,
// Tile.TILE_SIZE + 1, Tile.TILE_SIZE + 1, -2,
// -2, Tile.TILE_SIZE + 1, -2 };
private static final int NUM_VERTEX_SHORTS = 2;
private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
private static int STENCIL_BITS = 8;
// private static short[] mByteFillCoords = null;
private static PolygonLayer[] mFillPolys;
// static FloatBuffer compileLayerData(PolygonLayer layers, FloatBuffer buf) {
// FloatBuffer fbuf = buf;
// int size = 4;
//
// for (PolygonLayer l = layers; l != null; l = l.next)
// size += l.verticesCnt;
//
// size *= NUM_VERTEX_FLOATS;
//
// if (buf == null || buf.capacity() < size) {
// ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 4).order(
// ByteOrder.nativeOrder());
// // Log.d("GLMap", "allocate buffer " + size);
// fbuf = bbuf.asFloatBuffer();
// } else {
// fbuf.clear();
// }
//
// fbuf.put(mFillCoords, 0, 8);
// int pos = 4;
//
// PoolItem last = null, items = null;
//
// for (PolygonLayer l = layers; l != null; l = l.next) {
//
// for (PoolItem item = l.pool; item != null; item = item.next) {
// fbuf.put(item.vertices, 0, item.used);
// last = item;
// }
// l.offset = pos;
// pos += l.verticesCnt;
//
// if (last != null) {
// last.next = items;
// items = l.pool;
// }
//
// l.pool = null;
// }
//
// VertexPool.add(items);
//
// fbuf.flip();
//
// return fbuf;
// }
//
// static final short[] tmpItem = new short[PoolItem.SIZE];
private static int polygonProgram;
private static int hPolygonVertexPosition;
private static int hPolygonMatrix;
private static int hPolygonColor;
// static ShortBuffer compileLayerData(PolygonLayer layers, ShortBuffer buf) {
// ShortBuffer sbuf = buf;
// int size = 4;
static boolean init() {
// Set up the program for rendering polygons
polygonProgram = GlUtils.createProgram(Shaders.polygonVertexShader,
Shaders.polygonFragmentShader);
if (polygonProgram == 0) {
// Log.e(TAG, "Could not create polygon program.");
return false;
}
hPolygonMatrix = glGetUniformLocation(polygonProgram, "mvp");
hPolygonVertexPosition = glGetAttribLocation(polygonProgram, "a_position");
hPolygonColor = glGetUniformLocation(polygonProgram, "u_color");
mFillPolys = new PolygonLayer[STENCIL_BITS];
return true;
}
private static void fillPolygons(int count, double zoom, float scale) {
boolean blend = false;
// draw to framebuffer
glColorMask(true, true, true, true);
// do not modify stencil buffer
glStencilMask(0);
for (int c = 0; c < count; c++) {
PolygonLayer l = mFillPolys[c];
float alpha = 1.0f;
if (l.area.fade >= zoom || l.area.color[3] != 1.0) {
if (l.area.fade >= zoom) {
alpha = (scale > 1.3f ? scale : 1.3f) - alpha;
if (alpha > 1.0f)
alpha = 1.0f;
}
alpha *= l.area.color[3];
if (!blend) {
glEnable(GL_BLEND);
blend = true;
}
glUniform4f(hPolygonColor,
l.area.color[0], l.area.color[1], l.area.color[2], alpha);
} else if (l.area.blend == zoom) {
alpha = scale - 1.0f;
if (alpha > 1.0f)
alpha = 1.0f;
else if (alpha < 0)
alpha = 0;
glUniform4f(hPolygonColor,
l.area.color[0] * (1 - alpha) + l.area.blendColor[0] * alpha,
l.area.color[1] * (1 - alpha) + l.area.blendColor[1] * alpha,
l.area.color[2] * (1 - alpha) + l.area.blendColor[2] * alpha, 1);
} else {
if (blend) {
glDisable(GL_BLEND);
blend = false;
}
if (l.area.blend <= zoom && l.area.blend > 0)
glUniform4fv(hPolygonColor, 1, l.area.blendColor, 0);
else
glUniform4fv(hPolygonColor, 1, l.area.color, 0);
}
// set stencil buffer mask used to draw this layer
glStencilFunc(GL_EQUAL, 0xff, 1 << c);
// draw tile fill coordinates
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
if (blend)
glDisable(GL_BLEND);
}
static PolygonLayer drawPolygons(PolygonLayer layer, int next,
float[] matrix, double zoom, float scale, short drawCount, boolean clip) {
int cnt = 0;
glUseProgram(polygonProgram);
glEnableVertexAttribArray(hPolygonVertexPosition);
glVertexAttribPointer(hPolygonVertexPosition, 2,
GLES20.GL_SHORT, false, 0,
POLYGON_VERTICES_DATA_POS_OFFSET);
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
glEnable(GL_STENCIL_TEST);
PolygonLayer l = layer;
for (; l != null && l.layer < next; l = l.next) {
// fade out polygon layers (set in RederTheme)
if (l.area.fade > 0 && l.area.fade > zoom)
continue;
if (cnt == 0) {
// disable drawing to framebuffer
glColorMask(false, false, false, false);
// never pass the test, i.e. always apply first stencil op (sfail)
glStencilFunc(GL_NEVER, 0, 0xff);
// clear stencilbuffer
glStencilMask(0xFF);
// glClear(GL_STENCIL_BUFFER_BIT);
// clear stencilbuffer (tile region)
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// stencil op for stencil method polygon drawing
glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP);
}
mFillPolys[cnt] = l;
// set stencil mask to draw to
glStencilMask(1 << cnt++);
glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt);
// draw up to 8 layers into stencil buffer
if (cnt == STENCIL_BITS) {
fillPolygons(cnt, zoom, scale);
cnt = 0;
}
}
if (cnt > 0)
fillPolygons(cnt, zoom, scale);
glDisable(GL_STENCIL_TEST);
if (clip) {
drawDepthClip(drawCount);
}
// required on GalaxyII, Android 2.3.3 (cant just VAA enable once...)
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
return l;
}
// static void drawStencilClip(byte drawCount) {
// // set stencil mask for line drawing... HACK!!!
// glColorMask(false, false, false, false);
//
// for (PolygonLayer l = layers; l != null; l = l.next)
// size += l.verticesCnt;
// int c = drawCount % 16;
// // never pass the test, i.e. always apply first stencil op (sfail)
// // glStencilFunc(GLES20.GL_NEVER, drawCount, 0);
// glStencilFunc(GLES20.GL_NEVER, flipdabit[c], 0);
//
// size *= NUM_VERTEX_FLOATS;
// // replace stencilbuffer
// glStencilMask(0xff);
//
// if (buf == null || buf.capacity() < size) {
// ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 2).order(
// ByteOrder.nativeOrder());
// sbuf = bbuf.asShortBuffer();
// } else {
// sbuf.clear();
// }
// // set stencilbuffer for (tile region)
// glStencilOp(GLES20.GL_REPLACE, GLES20.GL_KEEP, GLES20.GL_KEEP);
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//
// short[] data = tmpItem;
// glStencilFunc(GL_EQUAL, flipdabit[c], 0xff);
// // do not modify stencil buffer
// glStencilMask(0);
//
// if (mByteFillCoords == null) {
// mByteFillCoords = new short[8];
// FastMath.convertFloatToHalf(mFillCoords[0], mByteFillCoords, 0);
// FastMath.convertFloatToHalf(mFillCoords[1], mByteFillCoords, 1);
// FastMath.convertFloatToHalf(mFillCoords[2], mByteFillCoords, 2);
// FastMath.convertFloatToHalf(mFillCoords[3], mByteFillCoords, 3);
// FastMath.convertFloatToHalf(mFillCoords[4], mByteFillCoords, 4);
// FastMath.convertFloatToHalf(mFillCoords[5], mByteFillCoords, 5);
// FastMath.convertFloatToHalf(mFillCoords[6], mByteFillCoords, 6);
// FastMath.convertFloatToHalf(mFillCoords[7], mByteFillCoords, 7);
// }
//
// sbuf.put(mByteFillCoords, 0, 8);
// int pos = 4;
//
// PoolItem last = null, items = null;
//
// for (PolygonLayer l = layers; l != null; l = l.next) {
//
// for (PoolItem item = l.pool; item != null; item = item.next) {
// PoolItem.toHalfFloat(item, data);
// sbuf.put(data, 0, item.used);
// last = item;
// }
//
// l.offset = pos;
// pos += l.verticesCnt;
//
// if (last != null) {
// last.next = items;
// items = l.pool;
// }
//
// l.pool = null;
// }
//
// VertexPool.add(items);
//
// sbuf.flip();
//
// return sbuf;
// }
//
// static void clear(PolygonLayer layers) {
// for (PolygonLayer l = layers; l != null; l = l.next) {
// if (l.pool != null)
// VertexPool.add(l.pool);
// }
// glColorMask(true, true, true, true);
// }
private static short[] mFillCoords;
// this only increases the chance for the stencil buffer clip to work
// should check for clipping with depth buffer or sth
// private static short[] flipdabit = {
// 255, 254, 253, 251,
// 247, 239, 223, 191,
// 127, 63, 252, 250,
// 249, 243, 231, 207 };
static ShortBuffer compileLayerData(PolygonLayer layers, ShortBuffer buf) {
ShortBuffer sbuf = buf;
int size = 4;
static void drawDepthClip(short drawCount) {
glColorMask(false, false, false, false);
glEnable(GLES20.GL_DEPTH_TEST);
glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
// GLES20.GL_PO
GLES20.glPolygonOffset(0, drawCount);
// int i[] = new int[1];
// GLES20.glGetIntegerv(GLES20.GL_POLYGON_OFFSET_UNITS, i, 0);
// Log.d("...", "UNITS " + i[0]);
//
// System.out.println("set offset: " + drawCount);
GLES20.glDepthMask(true);
GLES20.glDepthFunc(GLES20.GL_ALWAYS);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDepthMask(false);
glColorMask(true, true, true, true);
GLES20.glDepthFunc(GLES20.GL_EQUAL);
}
static int sizeOf(PolygonLayer layers) {
int size = 0;
for (PolygonLayer l = layers; l != null; l = l.next)
size += l.verticesCnt;
size *= NUM_VERTEX_FLOATS;
size *= NUM_VERTEX_SHORTS;
if (buf == null || buf.capacity() < size) {
ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 2).order(
ByteOrder.nativeOrder());
sbuf = bbuf.asShortBuffer();
} else {
sbuf.clear();
}
return size;
}
if (mFillCoords == null) {
short min = (-2) << 4;
short max = (short) ((Tile.TILE_SIZE + 1) << 4);
mFillCoords = new short[8];
mFillCoords[0] = min;
mFillCoords[1] = max;
mFillCoords[2] = max;
mFillCoords[3] = max;
mFillCoords[4] = min;
mFillCoords[5] = min;
mFillCoords[6] = max;
mFillCoords[7] = min;
}
sbuf.put(mFillCoords, 0, 8);
static void compileLayerData(PolygonLayer layers, ShortBuffer sbuf) {
int pos = 4;
ShortItem last = null, items = null;
@ -203,10 +300,6 @@ class PolygonLayers {
}
ShortPool.add(items);
sbuf.flip();
return sbuf;
}
static void clear(PolygonLayer layers) {

View File

@ -16,166 +16,141 @@
package org.mapsforge.android.glrenderer;
class Shaders {
final static String gLineVertexShader = ""
final static String lineVertexShader = ""
+ "precision mediump float; \n"
+ "uniform mat4 u_center;"
+ "uniform mat4 mvp;"
+ "attribute vec4 a_position;"
+ "attribute vec2 a_st;"
+ "varying vec2 v_st;"
+ "const vec4 scale = vec4(1.0/16.0, 1.0/16.0, 0.0, 1.0);"
+ "uniform float u_width;"
+ "uniform float u_offset;"
+ "const float dscale = 8.0/1000.0;"
+ "void main() {"
// + " gl_Position = u_center * vec4(a_position.x, a_position.y, 0.0, 1.0);"
// + " v_st = a_position.zw;"
+ " gl_Position = u_center * (scale * a_position);"
// + " gl_Position = u_center * a_position;"
+ " v_st = a_st;"
+ " vec2 dir = dscale * u_width * a_position.zw;"
+ " gl_Position = mvp * vec4(a_position.xy + dir, 0.0,1.0);"
+ " v_st = u_width * a_st;"
+ "}";
final static String gLineFragmentShader = ""
// final static String lineFragmentShader = ""
// + "precision mediump float;\n"
// + "uniform float u_wscale;"
// + "uniform float u_width;"
// + "uniform vec4 u_color;"
// + "varying vec2 v_st;"
// + "const float zero = 0.0;"
// + "void main() {"
// + " vec4 color = u_color;"
// + " float len;"
// + " if (v_st.t == zero)"
// + " len = abs(v_st.s);"
// + " else "
// + " len = length(v_st);"
// + " color.a *= smoothstep(zero, u_wscale, u_width - len);"
// + " gl_FragColor = color;"
// + "}";
final static String lineFragmentShader = ""
+ "#extension GL_OES_standard_derivatives : enable\n"
+ "precision mediump float;"
+ "uniform lowp vec2 u_mode;"
+ "precision mediump float;\n"
+ "uniform float u_wscale;"
+ "uniform float u_width;"
+ "uniform vec4 u_color;"
+ "const lowp float zero = 0.0;"
+ "const float fuzzf = 1.8;"
+ "varying vec2 v_st;"
+ "const float zero = 0.0;"
+ "void main() {"
+ " lowp vec4 color = u_color;"
+ " lowp float len;"
+ " lowp float fuzz;"
+ " lowp float width = u_mode[1];"
// + " if (v_st.t == zero){ "
// + " fuzz = fwidth(v_st.s) * fuzzf;"
// + " len = width - abs(v_st.s);"
// + " } else {"
+ " fuzz = max(fwidth(v_st.s), fwidth(v_st.t)) * fuzzf;"
+ " len = width - length(v_st);"
// + " } "
// + " if (len < min_fuzz)"
// + " discard;"
// + " alpha = zero;"
+ " if (len < fuzz) {"
+ " lowp float min_fuzz = -fuzz * u_mode[0];"
+ " color.a *= smoothstep(min_fuzz, fuzz, len);"
// + " if (color.a == 0.0 ) color = vec4(1.0,0.0,0.0,1.0);"
+ " }"
+ " vec4 color = u_color;"
+ " float width = u_width;"
+ " float len;"
+ " if (v_st.t == zero)"
+ " len = abs(v_st.s);"
+ " else "
+ " len = length(v_st);"
// + " if (u_width - len < 2.0){"
+ " vec2 st_width = fwidth(v_st);"
+ " float fuzz = max(st_width.s, st_width.t) * 1.5;"
+ " color.a *= smoothstep(zero, fuzz + u_wscale, u_width - len);"
// + " }"
+ " gl_FragColor = color;"
+ "}";
// final static String gLineFragmentShader = ""
// + "#extension GL_OES_standard_derivatives : enable\n"
// + "#pragma profilepragma blendoperation(gl_FragColor, GL_FUNC_ADD, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)\n"
// + "precision mediump float;"
// + "uniform vec2 u_mode;"
// + "uniform vec4 u_color;"
// + "const float zero = 0.0;"
// + "const vec4 blank = vec4(0.0,0.0,0.0,0.0);"
// + "varying vec2 v_st;"
// + "void main() {"
// + "lowp color = u_color;"
// + "lowp alpha = 1.0;"
// + "float width = u_mode[1];"
// + " if (v_st.t == zero){ "
// + " float fuzz = fwidth(v_st.s) * 1.5;"
// + " float min_fuzz = -fuzz * u_mode[0];"
// + " float len = width - abs(v_st.s);"
// // + " if (len > fuzz)"
// // + " gl_FragColor = u_color;"
// // + " else if (len < min_fuzz)"
// // + " gl_FragColor = blank;"
// // + " else"
// + " gl_FragColor = u_color * smoothstep(min_fuzz, fuzz, len);"
// + " } else {"
// + " float fuzz = max(fwidth(v_st.s), fwidth(v_st.t)) * 1.5;"
// + " gl_FragColor = u_color * smoothstep(-fuzz * u_mode[0], fuzz, width - length(v_st));"
// + " } "
// + "glFragColor = color"
// + "}";
// final static String gLineFragmentShader = ""
// + "#extension GL_OES_standard_derivatives : enable\n"
// + "precision mediump float;"
// + "uniform float u_width;"
// + "uniform vec2 u_mode;"
// + "uniform vec4 u_color;"
// + "const float zero = 0.0;"
// // + "const vec4 blank = vec4(1.0, 0.0, 0.0, 1.0);"
// + "varying vec2 v_st;"
// + "void main() {"
// + "float width = u_mode[1];"
// // + "float alpha = 1.0;"
// + " if (u_mode[0] == zero) {"
// // + " gl_FragColor = u_color;"
// // + " float fuzz;"
// // + " float len;"
// + " if (v_st.t == zero){ "
// // + " fuzz = - sqrt(dFdx(v_st.s) * dFdx(v_st.s) + dFdy(v_st.s) * dFdy(v_st.s));"
// + " float fuzz = -fwidth(v_st.s) * 1.5;"
// + " float len = abs(v_st.s) - width;"
// // + " if (len < fuzz)"
// + " gl_FragColor = u_color * smoothstep(zero, fuzz, len);"
// + " } else {"
// + " float fuzz = -max(fwidth(v_st.s), fwidth(v_st.t)) * 1.5;"
// + " float len = length(v_st) - width;"
// // + " if (len < fuzz)"
// + " gl_FragColor = u_color * smoothstep(zero, fuzz, len);"
// + " } "
// // + " if (len > zero)"
// // + " gl_FragColor = blank;"
// // + " discard;"
// // + " gl_FragColor = u_color;"
// // + " else if (len < fuzz)"
// // + " gl_FragColor = blank2;"
// // + " else "
// + " } else { "
// + " float fuzz = fwidth(v_st.s);"
// // + " gl_FragColor = u_color * smoothstep(fuzz, zero, abs(v_st.s) - u_width + fuzz);"
// // + " fuzz = - sqrt(dFdx(v_st.s) * dFdx(v_st.s) + dFdy(v_st.s) * dFdy(v_st.s)) * 1.5;"
// + " gl_FragColor = u_color * smoothstep(fuzz*0.5, -fuzz, abs(v_st.s) - width);"
// + " }"
// + "}";
//
// final static String gLineFragmentShader = "" +
// "#extension GL_OES_standard_derivatives : enable\n" +
// "precision mediump float;" +
// "uniform int u_mode;" +
// "uniform vec4 u_color;" +
// "varying vec2 v_st;" +
// "void main() {" +
// " gl_FragColor = u_color;" +
// "}";
final static String gLineFragmentShaderSimple = ""
final static String polygonVertexShader = ""
+ "precision mediump float;"
+ "uniform vec4 u_color;"
+ "uniform vec2 u_mode;"
+ "uniform float u_width;"
+ "varying vec2 v_st;"
+ "void main() {"
+ " gl_FragColor = u_color;"
+ " float width = u_width * u_mode[1];"
+ " float len;"
+ " if (v_st.t == 0.0) "
+ " len = abs(v_st.s);"
+ " else "
+ " len = length(v_st);"
+ " gl_FragColor.a = smoothstep(width, width - u_mode[1], width * len);"
+ "}";
final static String gPolygonVertexShader = ""
+ "precision mediump float; \n"
+ "uniform mat4 u_center;\n"
+ "uniform mat4 mvp;"
+ "attribute vec4 a_position;"
+ "const vec4 scale = vec4(1.0/16.0, 1.0/16.0, 0.0, 1.0);"
+ "uniform float u_offset;"
+ "void main() {"
+ " gl_Position = u_center * (scale * a_position);"
+ " gl_Position = mvp * a_position;"
+ "}";
final static String gPolygonFragmentShader = ""
final static String polygonFragmentShader = ""
+ "precision mediump float;"
+ "uniform vec4 u_color;"
+ "void main() {"
+ " gl_FragColor = u_color;"
+ "}";
final static String textVertexShader = ""
+ "precision highp float; "
+ "attribute vec4 vertex;"
+ "attribute vec2 tex_coord;"
+ "uniform mat4 mvp;"
+ "uniform float scale;"
+ "varying vec2 tex_c;"
+ "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
+ "void main() {"
+ " gl_Position = mvp * vec4(vertex.xy + (vertex.zw / scale), 0.0, 1.0);"
+ " tex_c = tex_coord * div;"
+ "}";
final static String textFragmentShader = ""
+ "precision highp float;"
+ "uniform sampler2D tex;"
+ "uniform vec4 col;"
+ "varying vec2 tex_c;"
+ "void main() {"
+ " gl_FragColor = texture2D(tex, tex_c.xy);"
+ "}";
// final static String lineVertexZigZagShader = ""
// + "precision mediump float; \n"
// + "uniform mat4 mvp;"
// + "attribute vec4 a_pos1;"
// + "attribute vec2 a_st1;"
// + "attribute vec4 a_pos2;"
// + "attribute vec2 a_st2;"
// + "varying vec2 v_st;"
// + "uniform vec2 u_mode;"
// + "const float dscale = 1.0/1000.0;"
// + "void main() {"
// + "if (gl_VertexID & 1 == 0) {"
// + " vec2 dir = dscale * u_mode[1] * a_pos1.zw;"
// + " gl_Position = mvp * vec4(a_pos1.xy + dir, 0.0,1.0);"
// + " v_st = u_mode[1] * a_st1;"
// + "} else {"
// + " vec2 dir = dscale * u_mode[1] * a_pos2.zw;"
// + " gl_Position = mvp * vec4( a_pos1.xy, dir, 0.0,1.0);"
// + " v_st = u_mode[1] * vec2(-a_st2.s , a_st2.t);"
// + "}";
// final static String lineFragmentShader = ""
// + "#extension GL_OES_standard_derivatives : enable\n"
// + "precision mediump float;\n"
// + "uniform vec2 u_mode;"
// + "uniform vec4 u_color;"
// + "varying vec2 v_st;"
// + "const float zero = 0.0;"
// + "void main() {"
// + " vec4 color = u_color;"
// + " float width = u_mode[1];"
// + " float len;"
// + " if (v_st.t == zero)"
// + " len = abs(v_st.s);"
// + " else "
// + " len = length(v_st);"
// + " vec2 st_width = fwidth(v_st);"
// + " float fuzz = max(st_width.s, st_width.t);"
// + " color.a *= smoothstep(-pixel, fuzz*pixel, width - (len * width));"
// + " gl_FragColor = color;"
// + "}";
}

View File

@ -24,5 +24,6 @@ public class ShortItem {
used = 0;
}
static int SIZE = 128;
// must be multiple of 6 and 4 (expected in LineLayer/PolygonLayer)
static final int SIZE = 240;
}

View File

@ -14,59 +14,80 @@
*/
package org.mapsforge.android.glrenderer;
import android.annotation.SuppressLint;
import android.util.Log;
public class ShortPool {
private static final int POOL_LIMIT = 8192;
@SuppressLint("UseValueOf")
private static final Boolean lock = new Boolean(true);
private static final int POOL_LIMIT = 6000;
static private ShortItem pool = null;
static private int count = 0;
static private int countAll = 0;
static ShortItem get() {
synchronized (lock) {
if (count == 0)
return new ShortItem();
count--;
ShortItem it = pool;
pool = pool.next;
it.used = 0;
it.next = null;
return it;
}
static synchronized void finish() {
count = 0;
countAll = 0;
pool = null;
}
static void add(ShortItem items) {
static synchronized ShortItem get() {
if (pool == null) {
countAll++;
return new ShortItem();
}
count--;
if (count < 0) {
int c = 0;
for (ShortItem tmp = pool; tmp != null; tmp = tmp.next)
c++;
Log.d("ShortPool", "eek wrong count: " + count + " left" + c);
return new ShortItem();
}
ShortItem it = pool;
pool = pool.next;
it.used = 0;
it.next = null;
return it;
}
static synchronized void add(ShortItem items) {
if (items == null)
return;
synchronized (lock) {
// limit pool items
if (countAll < POOL_LIMIT) {
ShortItem last = items;
// limit pool items
while (count < POOL_LIMIT) {
if (last.next == null) {
break;
}
last = last.next;
while (true) {
count++;
}
// clear references
ShortItem tmp2, tmp = last.next;
while (tmp != null) {
tmp2 = tmp;
tmp = tmp.next;
tmp2.next = null;
if (last.next == null)
break;
last = last.next;
}
last.next = pool;
pool = items;
} else {
// int cleared = 0;
ShortItem prev, tmp = items;
while (tmp != null) {
prev = tmp;
tmp = tmp.next;
countAll--;
prev.next = null;
}
}
}
}

View File

@ -15,6 +15,7 @@
package org.mapsforge.android.glrenderer;
import org.mapsforge.android.rendertheme.renderinstruction.Caption;
import org.mapsforge.android.rendertheme.renderinstruction.PathText;
public class TextItem {
TextItem next;
@ -22,14 +23,27 @@ public class TextItem {
final float x, y;
final String text;
final Caption caption;
final PathText path;
final float width;
short x1, y1, x2, y2;
public TextItem(float x, float y, String text, Caption caption) {
this.x = x;
this.y = y;
this.text = text;
this.caption = caption;
this.width = caption.paint.measureText(text);
this.path = null;
}
public TextItem(float x, float y, String text, PathText pathText, float width) {
this.x = x;
this.y = y;
this.text = text;
this.path = pathText;
this.caption = null;
this.width = width;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2012 Hannes Janetzek
*
* 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
@ -26,58 +26,59 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.FloatMath;
import android.util.Log;
public class TextRenderer {
private final static int TEXTURE_WIDTH = 512;
private final static int TEXTURE_HEIGHT = 256;
private final static float SCALE_FACTOR = 8.0f;
final static int MAX_LABELS = 30;
private final Bitmap mBitmap;
private final Canvas mCanvas;
private int mFontPadX = 1;
private int mFontPadY = 1;
private int mBitmapFormat;
private int mBitmapType;
private ByteBuffer mByteBuffer;
private ShortBuffer mShortBuffer;
private TextTexture[] mTextures;
private int mIndicesVBO;
private int mVerticesVBO;
final static int INDICES_PER_SPRITE = 6; // Indices Per Sprite
final static int VERTICES_PER_SPRITE = 4; // Vertices Per Sprite
final static int INDICES_PER_SPRITE = 6;
final static int VERTICES_PER_SPRITE = 4;
final static int SHORTS_PER_VERTICE = 6;
final static int MAX_LABELS = 35;
private static Bitmap mBitmap;
private static Canvas mCanvas;
private static int mFontPadX = 1;
private static int mFontPadY = 1;
private static int mBitmapFormat;
private static int mBitmapType;
private static ByteBuffer mByteBuffer;
private static ShortBuffer mShortBuffer;
private static TextTexture[] mTextures;
private static int mIndicesVBO;
private static int mVerticesVBO;
private static int mTextProgram;
static int hTextUVPMatrix;
static int hTextVertex;
static int hTextScale;
static int hTextTextureCoord;
static int hTextUColor;
private static int hTextUVPMatrix;
private static int hTextVertex;
private static int hTextScale;
private static int hTextTextureCoord;
// private static int hTextUColor;
static Paint mPaint = new Paint(Color.BLACK);
boolean debug = false;
short[] debugVertices = {
private static boolean debug = false;
private static short[] debugVertices = {
0, 0,
0, TEXTURE_HEIGHT,
0, TEXTURE_HEIGHT * 4,
0, TEXTURE_HEIGHT - 1,
0, 0,
TEXTURE_WIDTH - 1, 0,
TEXTURE_WIDTH, TEXTURE_HEIGHT,
TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4,
TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1,
TEXTURE_WIDTH, 0,
TEXTURE_WIDTH * 4, 0,
};
TextRenderer(int numTextures) {
static boolean init(int numTextures) {
mBitmap = Bitmap
.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
@ -85,15 +86,14 @@ public class TextRenderer {
mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
mBitmapType = GLUtils.getType(mBitmap);
mTextProgram = GlUtils.createProgram(textVertexShader, textFragmentShader);
mTextProgram = GlUtils.createProgram(Shaders.textVertexShader,
Shaders.textFragmentShader);
hTextUVPMatrix = GLES20.glGetUniformLocation(mTextProgram, "mvp");
hTextUColor = GLES20.glGetUniformLocation(mTextProgram, "col");
hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex");
hTextScale = GLES20.glGetUniformLocation(mTextProgram, "scale");
hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord");
// mVertexBuffer = new float[];
int bufferSize = numTextures
* MAX_LABELS * VERTICES_PER_SPRITE
* SHORTS_PER_VERTICE * (Short.SIZE / 8);
@ -121,12 +121,12 @@ public class TextRenderer {
GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
// load the generated bitmap onto the texture
// GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap,
mBitmapType, 0);
textures[i] = new TextTexture(textureIds[i]);
}
GlUtils.checkGlError("init textures");
mTextures = textures;
@ -136,12 +136,18 @@ public class TextRenderer {
int len = indices.length;
short j = 0;
for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) {
// indices[i + 0] = (short) (j + 0);
// indices[i + 1] = (short) (j + 1);
// indices[i + 2] = (short) (j + 2);
// indices[i + 3] = (short) (j + 2);
// indices[i + 4] = (short) (j + 3);
// indices[i + 5] = (short) (j + 0);
indices[i + 0] = (short) (j + 0);
indices[i + 1] = (short) (j + 1);
indices[i + 2] = (short) (j + 2);
indices[i + 3] = (short) (j + 2);
indices[i + 4] = (short) (j + 3);
indices[i + 5] = (short) (j + 0);
indices[i + 1] = (short) (j + 0);
indices[i + 2] = (short) (j + 1);
indices[i + 3] = (short) (j + 3);
indices[i + 4] = (short) (j + 2);
indices[i + 5] = (short) (j + 2);
}
ShortBuffer tmpIndices = mByteBuffer.asShortBuffer();
@ -151,21 +157,25 @@ public class TextRenderer {
int[] mVboIds = new int[2];
GLES20.glGenBuffers(2, mVboIds, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVboIds[0]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
tmpIndices,
GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
mIndicesVBO = mVboIds[0];
mVerticesVBO = mVboIds[1];
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
tmpIndices, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize,
mShortBuffer, GLES20.GL_DYNAMIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
return true;
}
boolean drawToTexture(GLMapTile tile) {
static boolean drawToTexture(GLMapTile tile) {
TextTexture tex = null;
if (tile.labels.size() == 0)
if (tile.labels == null)
return false;
for (int i = 0; i < mTextures.length; i++) {
@ -194,9 +204,7 @@ public class TextRenderer {
float x = mFontPadX;
float width, height;
int max = tile.labels.size();
if (max > MAX_LABELS)
max = MAX_LABELS;
int max = MAX_LABELS;
if (debug) {
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4],
@ -212,10 +220,18 @@ public class TextRenderer {
int advanceY = 0;
for (int i = 0; i < max; i++) {
TextItem t = tile.labels.get(i);
TextItem t = tile.labels;
float yy;
short x1, x2, x3, x4, y1, y2, y3, y4;
for (int i = 0; t != null && i < max; t = t.next, i++) {
if (t.caption != null) {
height = (int) (t.caption.fontHeight) + 2 * mFontPadY;
} else {
height = (int) (t.path.fontHeight) + 2 * mFontPadY;
}
height = (int) (t.caption.fontHeight) + 2 * mFontPadY;
width = t.width + 2 * mFontPadX;
if (height > advanceY)
@ -227,45 +243,71 @@ public class TextRenderer {
advanceY = (int) height;
}
float yy = y + (height - 1) - t.caption.fontDescent - mFontPadY;
if (t.caption != null) {
yy = y + (height - 1) - t.caption.fontDescent - mFontPadY;
} else {
yy = y + (height - 1) - t.path.fontDescent - mFontPadY;
}
if (yy > TEXTURE_HEIGHT) {
Log.d(TAG, "reached max labels");
continue;
}
if (t.caption.stroke != null)
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.stroke);
if (t.caption != null) {
if (t.caption.stroke != null)
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.stroke);
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.paint);
// Log.d(TAG, "draw: " + t.text + " at:" + (xx + t.width / 2) + " " + yy + " w:"
// + t.width + " " + cellHeight);
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.paint);
} else {
if (t.path.stroke != null)
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.stroke);
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.paint);
}
if (width > TEXTURE_WIDTH)
width = TEXTURE_WIDTH;
float halfWidth = width / 2.0f;
float halfHeight = height / 2.0f;
float hw = width / 2.0f;
float hh = height / 2.0f;
// short x1 = (short) (2.0f * (t.x - halfWidth));
// short y1 = (short) (2.0f * (t.y - halfHeight));
// short x2 = (short) (2.0f * (t.x + halfWidth));
// short y2 = (short) (2.0f * (t.y + halfHeight));
if (t.caption != null) {
x1 = x3 = (short) (SCALE_FACTOR * (-hw));
y1 = y3 = (short) (SCALE_FACTOR * (-hh));
x2 = x4 = (short) (SCALE_FACTOR * (hw));
y2 = y4 = (short) (SCALE_FACTOR * (hh));
}
else {
float vx = t.x1 - t.x2;
float vy = t.y1 - t.y2;
float a = FloatMath.sqrt(vx * vx + vy * vy);
vx = vx / a;
vy = vy / a;
short x1 = (short) (2.0f * (-halfWidth));
short y1 = (short) (2.0f * (-halfHeight));
short x2 = (short) (2.0f * (halfWidth));
short y2 = (short) (2.0f * (halfHeight));
float ux = -vy;
float uy = vx;
short u1 = (short) (2.0f * x);
short v1 = (short) (2.0f * y);
short u2 = (short) (2.0f * (x + width));
short v2 = (short) (2.0f * (y + height));
x1 = (short) (SCALE_FACTOR * (vx * hw + ux * hh));
y1 = (short) (SCALE_FACTOR * (vy * hw + uy * hh));
short tx = (short) (2.0f * t.x);
short ty = (short) (2.0f * t.y);
x2 = (short) (SCALE_FACTOR * (-vx * hw + ux * hh));
y3 = (short) (SCALE_FACTOR * (-vy * hw + uy * hh));
x4 = (short) (SCALE_FACTOR * (-vx * hw - ux * hh));
y4 = (short) (SCALE_FACTOR * (-vy * hw - uy * hh));
x3 = (short) (SCALE_FACTOR * (vx * hw - ux * hh));
y2 = (short) (SCALE_FACTOR * (vy * hw - uy * hh));
}
short u1 = (short) (SCALE_FACTOR * x);
short v1 = (short) (SCALE_FACTOR * y);
short u2 = (short) (SCALE_FACTOR * (x + width));
short v2 = (short) (SCALE_FACTOR * (y + height));
short tx = (short) (SCALE_FACTOR * t.x);
short ty = (short) (SCALE_FACTOR * t.y);
// top-left
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x1;
@ -273,23 +315,26 @@ public class TextRenderer {
buf[pos++] = u1;
buf[pos++] = v2;
// top-right
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x2;
buf[pos++] = y1;
buf[pos++] = y3;
buf[pos++] = u2;
buf[pos++] = v2;
// bot-right
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x2;
buf[pos++] = y2;
buf[pos++] = x4;
buf[pos++] = y4;
buf[pos++] = u2;
buf[pos++] = v1;
// bot-left
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x1;
buf[pos++] = x3;
buf[pos++] = y2;
buf[pos++] = u1;
buf[pos++] = v1;
@ -305,19 +350,16 @@ public class TextRenderer {
tex.length = pos;
tile.texture = tex;
tex.tile = tile;
// GlUtils.checkGlError("0");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id);
// GlUtils.checkGlError("1");
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, mBitmapFormat,
mBitmapType);
// GlUtils.checkGlError("2");
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap,
mBitmapFormat, mBitmapType);
return true;
}
private static String TAG = "TextRenderer";
void compileTextures() {
static void compileTextures() {
int offset = 0;
TextTexture tex;
@ -339,16 +381,18 @@ public class TextRenderer {
// Log.d(TAG, "compileTextures" + mFloatBuffer.remaining() + " " + offset);
// TODO use sub-bufferdata function
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, offset * (Short.SIZE / 8),
mShortBuffer, GLES20.GL_DYNAMIC_DRAW);
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8),
mShortBuffer);
}
void beginDraw(float scale) {
static void beginDraw(float scale) {
GLES20.glUseProgram(mTextProgram);
GLES20.glEnableVertexAttribArray(hTextTextureCoord);
GLES20.glEnableVertexAttribArray(hTextVertex);
GLES20.glUniform1f(hTextScale, scale);
if (debug) {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
mShortBuffer.clear();
@ -365,13 +409,13 @@ public class TextRenderer {
}
}
void endDraw() {
static void endDraw() {
GLES20.glDisableVertexAttribArray(hTextTextureCoord);
GLES20.glDisableVertexAttribArray(hTextVertex);
}
void drawTile(GLMapTile tile, float[] matrix) {
static void drawTile(GLMapTile tile, float[] matrix) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id);
@ -384,43 +428,12 @@ public class TextRenderer {
GLES20.glVertexAttribPointer(hTextVertex, 4,
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8));
// GLES20.glVertexAttribPointer(hTextVertexOffset, 2,
// GLES20.GL_SHORT, false, 8, tile.texture.offset * (Short.SIZE / 8) + 4);
//
GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)
+ 8);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) *
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, (tile.texture.length / 24) *
INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
}
}
private static String textVertexShader = ""
+ "precision highp float; "
+ "attribute vec4 vertex;"
// + "attribute vec4 offset;"
+ "attribute vec2 tex_coord;"
+ "uniform mat4 mvp;"
+ "uniform float scale;"
+ "varying vec2 tex_c;"
+ "const vec2 div = vec2(1.0/1024.0,1.0/512.0);"
+ "const vec4 dp = vec4(0.5,0.5,0.5,0.5);"
+ "void main() {"
+ " vec4 s = dp * vertex;"
+ " gl_Position = mvp * vec4(s.x + (s.z / scale), s.y + (s.w / scale), 0.0, 1.0);"
// + " gl_Position = vec4(pos.x + s.z, pos.y + s.w, 0.0, 1.0);"
+ " tex_c = tex_coord * div;"
+ "}";
private static String textFragmentShader = ""
+ "precision highp float;"
+ "uniform sampler2D tex;"
+ "uniform vec4 col;"
+ "varying vec2 tex_c;"
+ "void main() {"
+ " gl_FragColor = texture2D(tex, tex_c.xy);"
+ "}";
}

View File

@ -0,0 +1,285 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.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 <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.android.glrenderer;
import org.mapsforge.android.rendertheme.renderinstruction.PathText;
import org.mapsforge.android.utils.GeometryUtils;
import android.util.FloatMath;
final class WayDecorator {
// /**
// * Minimum distance in pixels before the symbol is repeated.
// */
// private static final int DISTANCE_BETWEEN_SYMBOLS = 200;
/**
* Minimum distance in pixels before the way name is repeated.
*/
private static final int DISTANCE_BETWEEN_WAY_NAMES = 500;
// /**
// * Distance in pixels to skip from both ends of a segment.
// */
// private static final int SEGMENT_SAFETY_DISTANCE = 30;
// static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter,
// boolean repeatSymbol, float[][] coordinates,
// List<SymbolContainer> waySymbols) {
// int skipPixels = SEGMENT_SAFETY_DISTANCE;
//
// // get the first way point coordinates
// float previousX = coordinates[0][0];
// float previousY = coordinates[0][1];
//
// // draw the symbol on each way segment
// float segmentLengthRemaining;
// float segmentSkipPercentage;
// float symbolAngle;
// for (int i = 2; i < coordinates[0].length; i += 2) {
// // get the current way point coordinates
// float currentX = coordinates[0][i];
// float currentY = coordinates[0][i + 1];
//
// // calculate the length of the current segment (Euclidian distance)
// float diffX = currentX - previousX;
// float diffY = currentY - previousY;
// double segmentLengthInPixel = Math.sqrt(diffX * diffX + diffY * diffY);
// segmentLengthRemaining = (float) segmentLengthInPixel;
//
// while (segmentLengthRemaining - skipPixels > SEGMENT_SAFETY_DISTANCE) {
// // calculate the percentage of the current segment to skip
// segmentSkipPercentage = skipPixels / segmentLengthRemaining;
//
// // move the previous point forward towards the current point
// previousX += diffX * segmentSkipPercentage;
// previousY += diffY * segmentSkipPercentage;
// symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY,
// currentX - previousX));
//
// waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY,
// alignCenter, symbolAngle));
//
// // check if the symbol should only be rendered once
// if (!repeatSymbol) {
// return;
// }
//
// // recalculate the distances
// diffX = currentX - previousX;
// diffY = currentY - previousY;
//
// // recalculate the remaining length of the current segment
// segmentLengthRemaining -= skipPixels;
//
// // set the amount of pixels to skip before repeating the symbol
// skipPixels = DISTANCE_BETWEEN_SYMBOLS;
// }
//
// skipPixels -= segmentLengthRemaining;
// if (skipPixels < SEGMENT_SAFETY_DISTANCE) {
// skipPixels = SEGMENT_SAFETY_DISTANCE;
// }
//
// // set the previous way point coordinates for the next loop
// previousX = currentX;
// previousY = currentY;
// }
// }
static TextItem renderText(float[] coordinates, String text, PathText pathText,
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 = 100;
int skipPixels = 0;
// get the first way point coordinates
int previousX = (int) coordinates[pos + 0];
int previousY = (int) coordinates[pos + 1];
// find way segments long enough to draw the way name on them
for (int i = pos + 2; i < pos + len; i += 2) {
// get the current way point coordinates
int currentX = (int) coordinates[i];
int currentY = (int) coordinates[i + 1];
// calculate the length of the current segment (Euclidian distance)
float diffX = currentX - previousX;
float diffY = currentY - previousY;
for (int j = i + 2; j < pos + len; j += 2) {
int nextX = (int) coordinates[j];
int nextY = (int) coordinates[j + 1];
if (diffY == 0) {
if ((currentY - nextY) != 0)
break;
currentX = nextX;
currentY = nextY;
continue;
} else if ((currentY - nextY) == 0)
break;
float diff = ((diffX) / (diffY) - (float) (currentX - nextX)
/ (currentY - nextY));
// skip segments with corners
if (diff >= 0.1f || diff <= -0.1f)
break;
currentX = nextX;
currentY = nextY;
}
diffX = currentX - previousX;
diffY = currentY - previousY;
if (diffX < 0)
diffX = -diffX;
if (diffY < 0)
diffY = -diffY;
if (diffX + diffY < minWidth) {
previousX = currentX;
previousY = currentY;
continue;
}
if (wayNameWidth > 0 && diffX + diffY < wayNameWidth) {
previousX = currentX;
previousY = currentY;
continue;
}
float segmentLengthInPixel = FloatMath.sqrt(diffX * diffX + diffY * diffY);
if (skipPixels > 0) {
skipPixels -= segmentLengthInPixel;
} else if (segmentLengthInPixel > minWidth) {
if (wayNameWidth < 0) {
wayNameWidth = pathText.paint.measureText(text);
}
if (segmentLengthInPixel > wayNameWidth + 25) {
float s = (wayNameWidth + 25) / segmentLengthInPixel;
int width, height;
int x1, y1, x2, y2;
if (previousX < currentX) {
x1 = previousX;
y1 = previousY;
x2 = currentX;
y2 = currentY;
} else {
x1 = currentX;
y1 = currentY;
x2 = previousX;
y2 = previousY;
}
// estimate position of text on path
width = (x2 - x1) / 2;
x2 = x2 - (int) (width - s * width);
x1 = x1 + (int) (width - s * width);
height = (y2 - y1) / 2;
y2 = y2 - (int) (height - s * height);
y1 = y1 + (int) (height - s * height);
short top = (short) (y1 < y2 ? y1 : y2);
short bot = (short) (y1 < y2 ? y2 : y1);
boolean intersects = false;
for (TextItem t2 = items; t2 != null; t2 = t2.next) {
// check crossings
if (GeometryUtils.lineIntersect(x1, y1, x2, y2, t2.x1, t2.y1,
t2.x2, t2.y2)) {
intersects = true;
break;
}
// check overlapping labels of road with more than one
// way
short top2 = (t2.y1 < t2.y2 ? t2.y1 : t2.y2);
short bot2 = (t2.y1 < t2.y2 ? t2.y2 : t2.y1);
if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2
&& top2 - 10 < bot) {
if (t2.text.equals(text)) {
intersects = true;
break;
}
}
}
if (intersects) {
previousX = (int) coordinates[pos + i];
previousY = (int) coordinates[pos + i + 1];
continue;
}
// Log.d("mapsforge", "add " + text + " " + first + " " + last);
if (previousX < currentX) {
x1 = previousX;
y1 = previousY;
x2 = currentX;
y2 = currentY;
} else {
x1 = currentX;
y1 = currentY;
x2 = previousX;
y2 = previousY;
}
// if (t == null)
t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, text,
pathText, wayNameWidth);
t.x1 = (short) x1;
t.y1 = (short) y1;
t.x2 = (short) x2;
t.y2 = (short) y2;
t.next = items;
items = t;
// skipPixels = DISTANCE_BETWEEN_WAY_NAMES;
return items;
}
}
// store the previous way point coordinates
previousX = currentX;
previousY = currentY;
}
return items;
}
private WayDecorator() {
throw new IllegalStateException();
}
}

View File

@ -19,14 +19,12 @@ import org.mapsforge.core.Tile;
import android.content.Context;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
/**
@ -220,27 +218,15 @@ public class TouchHandler {
}
// if (ret) {
// Log.d("", "" + );
//
// // try {
// //
// // Thread.sleep(10);
// // } catch (InterruptedException e) {
// // // TODO Auto-generated catch block
// // // e.printStackTrace();
// // }
//
// // throttle input
// long diff = SystemClock.uptimeMillis() - lastRun;
// if (diff < 16 && diff > 5) {
// // Log.d("", "" + diff);
// SystemClock.sleep(16 - diff);
// }
if (ret) {
// throttle input
long diff = SystemClock.uptimeMillis() - lastRun;
if (diff < 16) {
// Log.d("", "" + diff);
SystemClock.sleep(16 - diff);
}
lastRun = SystemClock.uptimeMillis();
}
// the event was not handled
// lastRun = SystemClock.uptimeMillis();
// }
return ret;
}
@ -250,7 +236,8 @@ public class TouchHandler {
private CountDownTimer mTimer = null;
public MapGestureDetector(MapView mapView) {
mScroller = new Scroller(mapView.getContext(), new DecelerateInterpolator());
mScroller = new Scroller(mapView.getContext(),
new android.view.animation.LinearInterpolator());
}
@Override
@ -284,8 +271,8 @@ public class TouchHandler {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
int w = Tile.TILE_SIZE * 20;
int h = Tile.TILE_SIZE * 20;
int w = Tile.TILE_SIZE * 10;
int h = Tile.TILE_SIZE * 10;
mPrevX = 0;
mPrevY = 0;
@ -297,7 +284,7 @@ public class TouchHandler {
mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2,
-w, w, -h, h);
// animate for two seconds
mTimer = new CountDownTimer(2000, 20) {
mTimer = new CountDownTimer(2000, 40) {
@Override
public void onTick(long tick) {
if (!scroll())

View File

@ -46,14 +46,14 @@ public class JobQueue {
* @param mapGeneratorJob
* the job to be added to this queue.
*/
public synchronized void addJob(MapGeneratorJob mapGeneratorJob) {
if (!mPriorityQueue.contains(mapGeneratorJob))
// priorityQueue.remove(mapGeneratorJob);
{
mapGeneratorJob.tile.isLoading = true;
mPriorityQueue.offer(mapGeneratorJob);
}
}
// public synchronized void addJob(MapGeneratorJob mapGeneratorJob) {
// if (!mPriorityQueue.contains(mapGeneratorJob))
// // priorityQueue.remove(mapGeneratorJob);
// {
// //mapGeneratorJob.tile.isLoading = true;
// mPriorityQueue.offer(mapGeneratorJob);
// }
// }
/**
* @param jobs

View File

@ -43,7 +43,7 @@ public class MapTile extends Tile {
/**
* distance from center, used in TileScheduler set by updateVisibleList.
*/
public long distance;
public float distance;
/**
* @param tileX

View File

@ -86,7 +86,7 @@ public class MapWorker extends PausableThread {
@Override
protected int getThreadPriority() {
return (Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 2;
return (Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 3;
// return mPrio;
}

View File

@ -17,6 +17,7 @@ package org.mapsforge.android.rendertheme;
import org.mapsforge.android.rendertheme.renderinstruction.Area;
import org.mapsforge.android.rendertheme.renderinstruction.Caption;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.android.rendertheme.renderinstruction.PathText;
import android.graphics.Bitmap;
import android.graphics.Paint;
@ -31,7 +32,7 @@ public interface IRenderCallback {
* @param area
* ...
*/
void renderArea(Area area);
void renderArea(Area area, int level);
/**
* Renders an area caption with the given text.
@ -83,7 +84,7 @@ public interface IRenderCallback {
* @param line
* ...
*/
void renderWay(Line line);
void renderWay(Line line, int level);
/**
* Renders a way with the given symbol along the way path.
@ -100,12 +101,8 @@ public interface IRenderCallback {
/**
* Renders a way with the given text along the way path.
*
* @param text
* the text to be rendered.
* @param paint
* the paint to be used for rendering the text.
* @param stroke
* an optional paint for the text casing (may be null).
* @param pathText
* ...
*/
void renderWayText(String text, Paint paint, Paint stroke);
void renderWayText(PathText pathText);
}

View File

@ -21,8 +21,9 @@ import org.mapsforge.core.Tag;
class NegativeMatcher implements AttributeMatcher {
private final String[] mKeyList;
private final String[] mValueList;
private final boolean mExclusive;
NegativeMatcher(List<String> keyList, List<String> valueList) {
NegativeMatcher(List<String> keyList, List<String> valueList, boolean exclusive) {
mKeyList = new String[keyList.size()];
for (int i = 0; i < mKeyList.length; i++)
mKeyList[i] = keyList.get(i).intern();
@ -30,6 +31,8 @@ class NegativeMatcher implements AttributeMatcher {
mValueList = new String[valueList.size()];
for (int i = 0; i < mValueList.length; i++)
mValueList[i] = valueList.get(i).intern();
mExclusive = exclusive;
}
@Override
@ -46,9 +49,9 @@ class NegativeMatcher implements AttributeMatcher {
for (Tag tag : tags) {
for (String value : mValueList)
if (value == tag.value)
return true;
return !mExclusive;
}
return false;
return mExclusive;
}
private boolean keyListDoesNotContainKeys(Tag[] tags) {

View File

@ -29,14 +29,14 @@ class NegativeRule extends Rule {
@Override
boolean matchesNode(Tag[] tags, byte zoomLevel) {
return mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
&& (mElement == Element.NODE || mElement == Element.ANY)
&& (mElement != Element.WAY)
&& mAttributeMatcher.matches(tags);
}
@Override
boolean matchesWay(Tag[] tags, byte zoomLevel, int closed) {
return mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
&& (mElement == Element.WAY || mElement == Element.ANY)
&& (mElement != Element.NODE)
&& (mClosed == closed || mClosed == Closed.ANY)
&& mAttributeMatcher.matches(tags);
}

View File

@ -37,16 +37,16 @@ class PositiveRule extends Rule {
@Override
boolean matchesNode(Tag[] tags, byte zoomLevel) {
return mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
&& (mElement == Element.NODE || mElement == Element.ANY)
return (mElement != Element.WAY)
&& mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
&& (mKeyMatcher == null || mKeyMatcher.matches(tags))
&& (mValueMatcher == null || mValueMatcher.matches(tags));
}
@Override
boolean matchesWay(Tag[] tags, byte zoomLevel, int closed) {
return mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
&& (mElement == Element.WAY || mElement == Element.ANY)
return (mElement != Element.NODE)
&& mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
&& (mClosed == closed || mClosed == Closed.ANY)
&& (mKeyMatcher == null || mKeyMatcher.matches(tags))
&& (mValueMatcher == null || mValueMatcher.matches(tags));

View File

@ -17,7 +17,6 @@ package org.mapsforge.android.rendertheme;
import java.util.ArrayList;
import java.util.List;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.android.rendertheme.renderinstruction.RenderInstruction;
import org.mapsforge.core.LRUCache;
import org.mapsforge.core.Tag;
@ -136,40 +135,112 @@ public class RenderTheme {
* ...
* @param zoomLevel
* ...
* @return ...
*/
public void matchNode(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel) {
// List<RenderInstruction> matchingList = matchingListNode;
// MatchingCacheKey matchingCacheKey = matchingCacheKeyNode;
public synchronized RenderInstruction[] matchNode(IRenderCallback renderCallback,
Tag[] tags,
byte zoomLevel) {
// if (!changed) {
// if (matchingList != null) {
// for (int i = 0, n = matchingList.size(); i < n; ++i) {
// matchingList.get(i).renderNode(renderCallback, tags);
// }
// }
// return;
// }
// matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
// matchingList = matchingCacheNodes.get(matchingCacheKey);
//
// if (matchingList != null) {
// // cache hit
// for (int i = 0, n = matchingList.size(); i < n; ++i) {
// matchingList.get(i).renderNode(renderCallback, tags);
// }
// } else {
RenderInstruction[] renderInstructions = null;
// cache miss
// matchingList = new ArrayList<RenderInstruction>();
for (int i = 0, n = mRulesList.size(); i < n; ++i) {
mRulesList.get(i).matchNode(renderCallback, tags, zoomLevel);
// , matchingList
MatchingCacheKey matchingCacheKey;
matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
boolean found = mMatchingCacheNodes.containsKey(matchingCacheKey);
if (found) {
renderInstructions = mMatchingCacheNodes.get(matchingCacheKey);
} else {
// cache miss
List<RenderInstruction> matchingList = new ArrayList<RenderInstruction>(4);
for (int i = 0, n = mRulesList.size(); i < n; ++i)
mRulesList.get(i)
.matchNode(renderCallback, tags, zoomLevel, matchingList);
int size = matchingList.size();
if (size > 0) {
renderInstructions = new RenderInstruction[size];
matchingList.toArray(renderInstructions);
}
mMatchingCacheNodes.put(matchingCacheKey, renderInstructions);
}
// matchingCacheNodes.put(matchingCacheKey, matchingList);
// }
//
// matchingListNode = matchingList;
// matchingCacheKeyNode = matchingCacheKey;
if (renderInstructions != null) {
for (int i = 0, n = renderInstructions.length; i < n; i++)
renderInstructions[i].renderNode(renderCallback, tags);
}
return renderInstructions;
}
/**
* Matches a way with the given parameters against this RenderTheme.
*
* @param renderCallback
* the callback implementation which will be executed on each match.
* @param tags
* the tags of the way.
* @param zoomLevel
* the zoom level at which the way should be matched.
* @param closed
* way is Closed
* @param render
* ...
* @return currently processed render instructions
*/
public synchronized RenderInstruction[] matchWay(IRenderCallback renderCallback,
Tag[] tags,
byte zoomLevel,
boolean closed, boolean render) {
RenderInstruction[] renderInstructions = null;
LRUCache<MatchingCacheKey, RenderInstruction[]> matchingCache;
MatchingCacheKey matchingCacheKey;
if (closed) {
matchingCache = mMatchingCacheArea;
} else {
matchingCache = mMatchingCacheWay;
}
matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
boolean found = matchingCache.containsKey(matchingCacheKey);
if (found) {
renderInstructions = matchingCache.get(matchingCacheKey);
} else {
// cache miss
int c = (closed ? Closed.YES : Closed.NO);
List<RenderInstruction> matchingList = new ArrayList<RenderInstruction>(4);
for (int i = 0, n = mRulesList.size(); i < n; ++i) {
mRulesList.get(i).matchWay(renderCallback, tags, zoomLevel, c,
matchingList);
}
int size = matchingList.size();
if (size > 0) {
renderInstructions = new RenderInstruction[size];
matchingList.toArray(renderInstructions);
}
matchingCache.put(matchingCacheKey, renderInstructions);
}
if (render && renderInstructions != null) {
for (int i = 0, n = renderInstructions.length; i < n; i++)
renderInstructions[i].renderWay(renderCallback, tags);
}
return renderInstructions;
}
void addRule(Rule rule) {
mRulesList.add(rule);
}
void complete() {
mRulesList.trimToSize();
for (int i = 0, n = mRulesList.size(); i < n; ++i) {
mRulesList.get(i).onComplete();
}
}
/**
@ -196,107 +267,6 @@ public class RenderTheme {
}
}
// private RenderInstruction[] mRenderInstructions = null;
/**
* Matches a way with the given parameters against this RenderTheme.
*
* @param renderCallback
* the callback implementation which will be executed on each match.
* @param tags
* the tags of the way.
* @param zoomLevel
* the zoom level at which the way should be matched.
* @param closed
* way is Closed
* @param changed
* ...
* @return currently processed render instructions
*/
public synchronized RenderInstruction[] matchWay(IRenderCallback renderCallback,
Tag[] tags,
byte zoomLevel,
boolean closed, boolean render) {
RenderInstruction[] renderInstructions = null;
LRUCache<MatchingCacheKey, RenderInstruction[]> matchingCache;
MatchingCacheKey matchingCacheKey;
// if (!changed) {
// renderInstructions = mRenderInstructions;
//
// if (renderInstructions != null) {
// for (int i = 0, n = renderInstructions.length; i < n; i++)
// renderInstructions[i].renderWay(renderCallback, tags);
// }
// return;
// }
if (closed) {
matchingCache = mMatchingCacheArea;
} else {
matchingCache = mMatchingCacheWay;
}
matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
boolean found = matchingCache.containsKey(matchingCacheKey);
if (found) {
renderInstructions = matchingCache.get(matchingCacheKey);
} else {
// cache miss
int c = (closed ? Closed.YES : Closed.NO);
List<RenderInstruction> matchingList = new ArrayList<RenderInstruction>(4);
for (int i = 0, n = mRulesList.size(); i < n; ++i) {
mRulesList.get(i).matchWay(renderCallback, tags, zoomLevel, c,
matchingList);
}
int size = matchingList.size();
if (size > 0) {
renderInstructions = new RenderInstruction[matchingList.size()];
for (int i = 0, n = matchingList.size(); i < n; ++i) {
RenderInstruction renderInstruction = matchingList.get(i);
renderInstructions[i] = renderInstruction;
}
}
matchingCache.put(matchingCacheKey, renderInstructions);
}
if (render && renderInstructions != null) {
for (int i = 0, n = renderInstructions.length; i < n; i++)
renderInstructions[i].renderWay(renderCallback, tags);
}
// mRenderInstructions = renderInstructions;
return renderInstructions;
}
void addRule(Rule rule) {
mRulesList.add(rule);
}
void complete() {
mRulesList.trimToSize();
for (int i = 0, n = mRulesList.size(); i < n; ++i) {
mRulesList.get(i).onComplete();
}
}
private final ArrayList<Line> outlineLayers = new ArrayList<Line>();
void addOutlineLayer(Line line) {
outlineLayers.add(line);
}
/**
* @param layer
* ...
* @return Line (paint and level) used for outline
*/
public Line getOutline(int layer) {
return outlineLayers.get(layer);
}
void setLevels(int levels) {
mLevels = levels;
}

View File

@ -16,6 +16,7 @@ package org.mapsforge.android.rendertheme;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -26,9 +27,11 @@ import javax.xml.parsers.SAXParserFactory;
import org.mapsforge.android.rendertheme.renderinstruction.Area;
import org.mapsforge.android.rendertheme.renderinstruction.Caption;
import org.mapsforge.android.rendertheme.renderinstruction.Circle;
import org.mapsforge.android.rendertheme.renderinstruction.AreaLevel;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.android.rendertheme.renderinstruction.LineSymbol;
import org.mapsforge.android.rendertheme.renderinstruction.PathText;
import org.mapsforge.android.rendertheme.renderinstruction.RenderInstruction;
import org.mapsforge.android.rendertheme.renderinstruction.Symbol;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
@ -41,14 +44,23 @@ import org.xml.sax.helpers.DefaultHandler;
* SAX2 handler to parse XML render theme files.
*/
public class RenderThemeHandler extends DefaultHandler {
private static final Logger LOG = Logger.getLogger(RenderThemeHandler.class.getName());
private static final Logger LOG = Logger
.getLogger(RenderThemeHandler.class.getName());
private static enum Element {
RENDER_THEME, RENDERING_INSTRUCTION, RULE;
RENDER_THEME, RENDERING_INSTRUCTION, RULE, STYLE;
}
private static final String ELEMENT_NAME_RENDER_THEME = "rendertheme";
private static final String ELEMENT_NAME_RULE = "rule";
private static final String ELEMENT_NAME_STYPE_PATH_TEXT = "style-pathtext";
private static final String ELEMENT_NAME_STYLE_AREA = "style-area";
private static final String ELEMENT_NAME_STYLE_LINE = "style-line";
private static final String ELEMENT_NAME_STYLE_OUTLINE = "style-outline";
private static final String ELEMENT_NAME_USE_STYLE_PATH_TEXT = "use-text";
private static final String ELEMENT_NAME_USE_STYLE_AREA = "use-area";
private static final String ELEMENT_NAME_USE_STYLE_LINE = "use-line";
private static final String ELEMENT_NAME_USE_STYLE_OUTLINE = "use-outline";
private static final String UNEXPECTED_ELEMENT = "unexpected element: ";
/**
@ -62,10 +74,12 @@ public class RenderThemeHandler extends DefaultHandler {
* @throws IOException
* if an I/O error occurs while reading from the input stream.
*/
public static RenderTheme getRenderTheme(InputStream inputStream) throws SAXException,
public static RenderTheme getRenderTheme(InputStream inputStream)
throws SAXException,
ParserConfigurationException, IOException {
RenderThemeHandler renderThemeHandler = new RenderThemeHandler();
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser()
.getXMLReader();
xmlReader.setContentHandler(renderThemeHandler);
xmlReader.parse(new InputSource(inputStream));
return renderThemeHandler.mRenderTheme;
@ -83,7 +97,8 @@ public class RenderThemeHandler extends DefaultHandler {
* @param attributeIndex
* the XML attribute index position.
*/
public static void logUnknownAttribute(String element, String name, String value, int attributeIndex) {
public static void logUnknownAttribute(String element, String name, String value,
int attributeIndex) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("unknown attribute in element ");
stringBuilder.append(element);
@ -110,6 +125,7 @@ public class RenderThemeHandler extends DefaultHandler {
mRenderTheme.setLevels(mLevel);
mRenderTheme.complete();
tmpStyleHash.clear();
}
@Override
@ -131,8 +147,12 @@ public class RenderThemeHandler extends DefaultHandler {
LOG.log(Level.SEVERE, null, exception);
}
private static HashMap<String, RenderInstruction> tmpStyleHash = new HashMap<String, RenderInstruction>(
10);
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
try {
if (ELEMENT_NAME_RENDER_THEME.equals(localName)) {
checkState(localName, Element.RENDER_THEME);
@ -149,10 +169,53 @@ public class RenderThemeHandler extends DefaultHandler {
mRuleStack.push(mCurrentRule);
}
else if (ELEMENT_NAME_STYPE_PATH_TEXT.equals(localName)) {
checkState(localName, Element.STYLE);
PathText pathText = PathText.create(localName, attributes);
tmpStyleHash.put("t" + pathText.style, pathText);
// System.out.println("add style: " + pathText.style);
}
else if (ELEMENT_NAME_STYLE_AREA.equals(localName)) {
checkState(localName, Element.STYLE);
Area area = Area.create(localName, attributes, 0);
tmpStyleHash.put("a" + area.style, area);
// System.out.println("add style: " + area.style);
}
else if (ELEMENT_NAME_STYLE_LINE.equals(localName)) {
checkState(localName, Element.STYLE);
String style = null;
if ((style = attributes.getValue("from")) != null) {
RenderInstruction ri = tmpStyleHash.get("l" + style);
if (ri instanceof Line) {
Line line = Line.create((Line) ri, localName, attributes, 0,
false);
tmpStyleHash.put("l" + line.style, line);
// System.out.println("add style: " + line.style + " from " + style);
}
else {
// System.out.println("couldnt check the style yo! " + style);
}
} else {
Line line = Line.create(null, localName, attributes, 0, false);
tmpStyleHash.put("l" + line.style, line);
// System.out.println("add style: " + line.style);
}
}
else if (ELEMENT_NAME_STYLE_OUTLINE.equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
Line line = Line.create(null, localName, attributes, mLevel++, true);
tmpStyleHash.put("o" + line.style, line);
// outlineLayers.add(line);
}
else if ("area".equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
Area area = Area.create(localName, attributes, mLevel++);
mRuleStack.peek().addRenderingInstruction(area);
// mRuleStack.peek().addRenderingInstruction(area);
mCurrentRule.addRenderingInstruction(area);
}
else if ("caption".equals(localName)) {
@ -169,17 +232,10 @@ public class RenderThemeHandler extends DefaultHandler {
else if ("line".equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
Line line = Line.create(localName, attributes, mLevel++);
Line line = Line.create(null, localName, attributes, mLevel++, false);
mCurrentRule.addRenderingInstruction(line);
}
else if ("outline".equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
Line line = Line.create(localName, attributes, mLevel++);
mRenderTheme.addOutlineLayer(line);
// mCurrentRule.addRenderingInstruction(line);
}
else if ("lineSymbol".equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
LineSymbol lineSymbol = LineSymbol.create(localName, attributes);
@ -198,7 +254,45 @@ public class RenderThemeHandler extends DefaultHandler {
mCurrentRule.addRenderingInstruction(symbol);
}
else {
else if (ELEMENT_NAME_USE_STYLE_LINE.equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
String style = attributes.getValue("name");
if (style != null) {
Line line = (Line) tmpStyleHash.get("l" + style);
if (line != null) {
// System.out.println("found style line : " + line.style);
Line newLine = Line.create(line, localName, attributes,
mLevel++, false);
mCurrentRule.addRenderingInstruction(newLine);
}
}
} else if (ELEMENT_NAME_USE_STYLE_OUTLINE.equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
String style = attributes.getValue("name");
if (style != null) {
Line line = (Line) tmpStyleHash.get("o" + style);
if (line != null && line.outline)
mCurrentRule.addRenderingInstruction(line);
}
} else if (ELEMENT_NAME_USE_STYLE_AREA.equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
String style = attributes.getValue("name");
if (style != null) {
Area area = (Area) tmpStyleHash.get("a" + style);
if (area != null)
mCurrentRule.addRenderingInstruction(new AreaLevel(area,
mLevel++));
}
} else if (ELEMENT_NAME_USE_STYLE_PATH_TEXT.equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION);
String style = attributes.getValue("name");
if (style != null) {
PathText pt = (PathText) tmpStyleHash.get("t" + style);
if (pt != null)
mCurrentRule.addRenderingInstruction(pt);
}
} else {
throw new SAXException("unknown element: " + localName);
}
} catch (IllegalArgumentException e) {
@ -214,6 +308,7 @@ public class RenderThemeHandler extends DefaultHandler {
}
private void checkElement(String elementName, Element element) throws SAXException {
Element parentElement;
switch (element) {
case RENDER_THEME:
if (!mElementStack.empty()) {
@ -222,8 +317,16 @@ public class RenderThemeHandler extends DefaultHandler {
return;
case RULE:
Element parentElement = mElementStack.peek();
if (parentElement != Element.RENDER_THEME && parentElement != Element.RULE) {
parentElement = mElementStack.peek();
if (parentElement != Element.RENDER_THEME
&& parentElement != Element.RULE) {
throw new SAXException(UNEXPECTED_ELEMENT + elementName);
}
return;
case STYLE:
parentElement = mElementStack.peek();
if (parentElement != Element.RENDER_THEME) {
throw new SAXException(UNEXPECTED_ELEMENT + elementName);
}
return;

View File

@ -32,6 +32,7 @@ abstract class Rule {
private static final Map<List<String>, AttributeMatcher> MATCHERS_CACHE_VALUE = new HashMap<List<String>, AttributeMatcher>();
private static final Pattern SPLIT_PATTERN = Pattern.compile("\\|");
private static final String STRING_NEGATION = "~";
private static final String STRING_EXCLUSIVE = "-";
private static final String STRING_WILDCARD = "*";
private static Rule createRule(Stack<Rule> ruleStack, int element, String keys,
@ -44,11 +45,18 @@ abstract class Rule {
.split(values)));
if (valueList.remove(STRING_NEGATION)) {
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList);
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList,
false);
return new NegativeRule(element, closed, zoomMin, zoomMax,
attributeMatcher);
}
if (valueList.remove(STRING_EXCLUSIVE)) {
AttributeMatcher attributeMatcher = new NegativeMatcher(keyList, valueList,
true);
return new NegativeRule(element, closed, zoomMin, zoomMax,
attributeMatcher);
}
AttributeMatcher keyMatcher = getKeyMatcher(keyList);
AttributeMatcher valueMatcher = getValueMatcher(valueList);
@ -189,13 +197,14 @@ abstract class Rule {
abstract boolean matchesWay(Tag[] tags, byte zoomLevel, int closed);
void matchNode(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel) {
void matchNode(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel,
List<RenderInstruction> matchingList) {
if (matchesNode(tags, zoomLevel)) {
for (int i = 0, n = mRenderInstructionArray.length; i < n; i++)
mRenderInstructionArray[i].renderNode(renderCallback, tags);
matchingList.add(mRenderInstructionArray[i]);
for (int i = 0, n = mSubRuleArray.length; i < n; i++)
mSubRuleArray[i].matchNode(renderCallback, tags, zoomLevel);
mSubRuleArray[i].matchNode(renderCallback, tags, zoomLevel, matchingList);
}
}
@ -220,14 +229,15 @@ abstract class Rule {
MATCHERS_CACHE_VALUE.clear();
mRenderInstructionArray = new RenderInstruction[mRenderInstructions.size()];
for (int i = 0, n = mRenderInstructions.size(); i < n; i++)
mRenderInstructionArray[i] = mRenderInstructions.get(i);
mRenderInstructions.toArray(mRenderInstructionArray);
// for (int i = 0, n = mRenderInstructions.size(); i < n; i++)
// mRenderInstructionArray[i] = mRenderInstructions.get(i);
mSubRuleArray = new Rule[mSubRules.size()];
mSubRules.toArray(mSubRuleArray);
for (int i = 0, n = mSubRules.size(); i < n; i++)
mSubRuleArray[i] = mSubRules.get(i);
// for (int i = 0, n = mSubRules.size(); i < n; i++)
// mSubRuleArray[i] = mSubRules.get(i);
mRenderInstructions.clear();
mRenderInstructions = null;

File diff suppressed because it is too large Load Diff

View File

@ -132,11 +132,11 @@
<xs:attribute name="src" type="tns:src" use="optional" />
<xs:attribute name="stroke" type="tns:color" use="optional"
default="#000000" />
<xs:attribute name="stroke-width" type="tns:nonNegativeFloat"
<xs:attribute name="width" type="tns:nonNegativeFloat"
use="optional" default="0" />
<xs:attribute name="stroke-dasharray" type="tns:strokeDasharray"
use="optional" />
<xs:attribute name="stroke-linecap" type="tns:cap" use="optional"
<xs:attribute name="cap" type="tns:cap" use="optional"
default="round" />
<xs:attribute name="outline" type="xs:integer" use="optional"
default="0" />
@ -163,6 +163,7 @@
</xs:complexType>
<xs:complexType name="pathText">
<xs:attribute name="style" type="xs:string" use="optional" default="0"/>
<xs:attribute name="k" type="tns:textKey" use="required" />
<xs:attribute name="dy" type="xs:float" use="optional"
default="0" />
@ -190,7 +191,7 @@
<xs:choice minOccurs="0" maxOccurs="unbounded">
<!-- recursion to allow for nested rules -->
<xs:element name="rule" type="tns:rule" />
<xs:element name="area" type="tns:area" />
<xs:element name="caption" type="tns:caption" />
<xs:element name="circle" type="tns:circle" />
@ -198,6 +199,7 @@
<xs:element name="outline" type="tns:outline" />
<xs:element name="lineSymbol" type="tns:lineSymbol" />
<xs:element name="pathText" type="tns:pathText" />
<xs:element name="stylePathText type="xs:string" />
<xs:element name="symbol" type="tns:symbol" />
</xs:choice>
<xs:attribute name="e" type="tns:elementList" use="required" />
@ -214,6 +216,12 @@
<!-- rendertheme element -->
<xs:complexType name="rendertheme">
<xs:sequence minOccurs="0" maxOccurds="256">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="style-pathtext" type="tns:pathText" />
<xs:element name="style-area" type="tns:area" />
</xs:choice>
</xs:sequence)
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="rule" type="tns:rule" />
</xs:sequence>

View File

@ -14,23 +14,17 @@
*/
package org.mapsforge.android.rendertheme.renderinstruction;
import java.io.IOException;
import org.mapsforge.android.rendertheme.IRenderCallback;
import org.mapsforge.android.rendertheme.RenderThemeHandler;
import org.mapsforge.core.Tag;
import org.xml.sax.Attributes;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.Shader;
/**
* Represents a closed polygon on the map.
*/
public final class Area implements RenderInstruction {
public final class Area extends RenderInstruction {
/**
* @param elementName
* the name of the XML element.
@ -39,11 +33,8 @@ public final class Area implements RenderInstruction {
* @param level
* the drawing level of this instruction.
* @return a new Area with the given rendering attributes.
* @throws IOException
* if an I/O error occurs while reading a resource.
*/
public static Area create(String elementName, Attributes attributes, int level)
throws IOException {
public static Area create(String elementName, Attributes attributes, int level) {
String src = null;
int fill = Color.BLACK;
int stroke = Color.TRANSPARENT;
@ -51,12 +42,14 @@ public final class Area implements RenderInstruction {
int fade = -1;
int blend = -1;
int blendFill = Color.BLACK;
String style = null;
for (int i = 0; i < attributes.getLength(); ++i) {
String name = attributes.getLocalName(i);
String value = attributes.getValue(i);
if ("src".equals(name)) {
if ("name".equals(name))
style = value;
else if ("src".equals(name)) {
src = value;
} else if ("fill".equals(name)) {
fill = Color.parseColor(value);
@ -76,7 +69,8 @@ public final class Area implements RenderInstruction {
}
validate(strokeWidth);
return new Area(src, fill, stroke, strokeWidth, fade, level, blend, blendFill);
return new Area(style, src, fill, stroke, strokeWidth, fade, level, blend,
blendFill);
}
private static void validate(float strokeWidth) {
@ -86,32 +80,38 @@ public final class Area implements RenderInstruction {
}
}
private Area(String src, int fill, int stroke, float strokeWidth, int fade,
int level, int blend, int blendFill)
throws IOException {
private Area(String style, String src, int fill, int stroke, float strokeWidth,
int fade, int level, int blend, int blendFill) {
super();
this.style = style;
if (fill == Color.TRANSPARENT) {
paintFill = null;
} else {
paintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
if (src != null) {
Shader shader = BitmapUtils.createBitmapShader(src);
paintFill.setShader(shader);
}
paintFill.setStyle(Style.FILL);
paintFill.setColor(fill);
paintFill.setStrokeCap(Cap.ROUND);
}
// if (fill == Color.TRANSPARENT) {
// paintFill = null;
// } else {
// paintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
// if (src != null) {
// Shader shader = BitmapUtils.createBitmapShader(src);
// paintFill.setShader(shader);
// }
// paintFill.setStyle(Style.FILL);
// paintFill.setColor(fill);
// paintFill.setStrokeCap(Cap.ROUND);
// }
//
// if (stroke == Color.TRANSPARENT) {
// paintOutline = null;
// } else {
// paintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
// paintOutline.setStyle(Style.STROKE);
// paintOutline.setColor(stroke);
// paintOutline.setStrokeCap(Cap.ROUND);
// }
if (stroke == Color.TRANSPARENT) {
paintOutline = null;
} else {
paintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
paintOutline.setStyle(Style.STROKE);
paintOutline.setColor(stroke);
paintOutline.setStrokeCap(Cap.ROUND);
}
// if (stroke == Color.TRANSPARENT) {
// stroke = null;
// } else{
// stroke = new Line()
// }
color = new float[4];
color[0] = (fill >> 16 & 0xff) / 255.0f;
@ -135,47 +135,31 @@ public final class Area implements RenderInstruction {
this.level = level;
}
@Override
public void destroy() {
// do nothing
}
@Override
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
// do nothing
}
@Override
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
if (paintFill != null) {
renderCallback.renderArea(this);
}
renderCallback.renderArea(this, this.level);
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
if (paintOutline != null) {
paintOutline.setStrokeWidth(strokeWidth * scaleFactor);
}
}
@Override
public void scaleTextSize(float scaleFactor) {
// do nothing
}
// @Override
// public void scaleStrokeWidth(float scaleFactor) {
// // if (paintOutline != null) {
// // paintOutline.setStrokeWidth(strokeWidth * scaleFactor);
// // }
// }
public String style;
/**
*
*/
public final int level;
private final int level;
/**
*
*/
public final Paint paintFill;
// public final Paint paintFill;
/**
*
*/
public final Paint paintOutline;
// public final Paint paintOutline;
/**
*
*/

View File

@ -12,17 +12,22 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mapsforge.android.glrenderer;
package org.mapsforge.android.rendertheme.renderinstruction;
import java.util.ArrayList;
import org.mapsforge.android.rendertheme.IRenderCallback;
import org.mapsforge.core.Tag;
import org.mapsforge.android.rendertheme.renderinstruction.Caption;
public class TextLayer {
public ArrayList<TextItem> labels;
void addLabel(float x, float y, String text, Caption caption) {
public class AreaLevel extends RenderInstruction {
private final Area area;
private final int level;
public AreaLevel(Area area, int level) {
this.area = area;
this.level = level;
}
@Override
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
renderCallback.renderArea(this.area, level);
}
}

View File

@ -32,7 +32,7 @@ import android.util.FloatMath;
/**
* Represents a text label on the map.
*/
public final class Caption implements RenderInstruction {
public final class Caption extends RenderInstruction {
/**
* @param elementName
* the name of the XML element.
@ -130,11 +130,6 @@ public final class Caption implements RenderInstruction {
fontDescent = FloatMath.ceil(Math.abs(fm.descent));
}
@Override
public void destroy() {
// do nothing
}
@Override
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
renderCallback.renderPointOfInterestCaption(this);
@ -145,11 +140,6 @@ public final class Caption implements RenderInstruction {
renderCallback.renderAreaCaption(this);
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
// do nothing
}
@Override
public void scaleTextSize(float scaleFactor) {
paint.setTextSize(fontSize * scaleFactor);

View File

@ -26,7 +26,7 @@ import android.graphics.Paint.Style;
/**
* Represents a round area on the map.
*/
public final class Circle implements RenderInstruction {
public final class Circle extends RenderInstruction {
/**
* @param elementName
* the name of the XML element.
@ -68,11 +68,13 @@ public final class Circle implements RenderInstruction {
private static void validate(String elementName, Float radius, float strokeWidth) {
if (radius == null) {
throw new IllegalArgumentException("missing attribute r for element: " + elementName);
throw new IllegalArgumentException("missing attribute r for element: "
+ elementName);
} else if (radius.floatValue() < 0) {
throw new IllegalArgumentException("radius must not be negative: " + radius);
} else if (strokeWidth < 0) {
throw new IllegalArgumentException("stroke-width must not be negative: " + strokeWidth);
throw new IllegalArgumentException("stroke-width must not be negative: "
+ strokeWidth);
}
}
@ -84,7 +86,8 @@ public final class Circle implements RenderInstruction {
private final boolean mScaleRadius;
private final float mStrokeWidth;
private Circle(Float radius, boolean scaleRadius, int fill, int stroke, float strokeWidth, int level) {
private Circle(Float radius, boolean scaleRadius, int fill, int stroke,
float strokeWidth, int level) {
super();
mRadius = radius.floatValue();
@ -117,11 +120,6 @@ public final class Circle implements RenderInstruction {
}
}
@Override
public void destroy() {
// do nothing
}
@Override
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
if (mOutline != null) {
@ -132,11 +130,6 @@ public final class Circle implements RenderInstruction {
}
}
@Override
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
// do nothing
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
if (mScaleRadius) {
@ -146,9 +139,4 @@ public final class Circle implements RenderInstruction {
}
}
}
@Override
public void scaleTextSize(float scaleFactor) {
// do nothing
}
}

View File

@ -14,7 +14,6 @@
*/
package org.mapsforge.android.rendertheme.renderinstruction;
import java.io.IOException;
import java.util.Locale;
import java.util.regex.Pattern;
@ -24,73 +23,94 @@ import org.mapsforge.core.Tag;
import org.xml.sax.Attributes;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.Shader;
/**
* Represents a polyline on the map.
*/
public final class Line implements RenderInstruction {
public final class Line extends RenderInstruction {
private static final Pattern SPLIT_PATTERN = Pattern.compile(",");
/**
* @param line
* ...
* @param elementName
* the name of the XML element.
* @param attributes
* the attributes of the XML element.
* @param level
* the drawing level of this instruction.
* @param isOutline
* ...
* @return a new Line with the given rendering attributes.
* @throws IOException
* if an I/O error occurs while reading a resource.
*/
public static Line create(String elementName, Attributes attributes, int level)
throws IOException {
public static Line create(Line line, String elementName, Attributes attributes,
int level, boolean isOutline) {
String src = null;
int stroke = Color.BLACK;
float strokeWidth = 0;
float[] strokeDasharray = null;
Cap strokeLinecap = Cap.ROUND;
int outline = -1;
int fade = -1;
boolean fixed = false;
String style = null;
float blur = 0;
if (line != null) {
fixed = line.fixed;
fade = line.fade;
strokeLinecap = line.cap;
blur = line.blur;
}
for (int i = 0; i < attributes.getLength(); ++i) {
String name = attributes.getLocalName(i);
String value = attributes.getValue(i);
if ("src".equals(name)) {
if ("name".equals(name))
style = value;
else if ("src".equals(name)) {
src = value;
} else if ("stroke".equals(name)) {
stroke = Color.parseColor(value);
} else if ("stroke-width".equals(name)) {
} else if ("width".equals(name)) {
strokeWidth = Float.parseFloat(value);
} else if ("stroke-dasharray".equals(name)) {
strokeDasharray = parseFloatArray(value);
} else if ("stroke-linecap".equals(name)) {
} else if ("cap".equals(name)) {
strokeLinecap = Cap.valueOf(value.toUpperCase(Locale.ENGLISH));
} else if ("outline".equals(name)) {
outline = Integer.parseInt(value);
} else if ("fade".equals(name)) {
fade = Integer.parseInt(value);
} else if ("fixed".equals(name)) {
fixed = Boolean.parseBoolean(value);
} else if ("blur".equals(name)) {
blur = Float.parseFloat(value);
} else if ("from".equals(name)) {
} else {
RenderThemeHandler.logUnknownAttribute(elementName, name, value, i);
}
}
validate(strokeWidth);
return new Line(src, stroke, strokeWidth, strokeDasharray, strokeLinecap, level,
outline, fixed, fade);
if (line != null) {
strokeWidth = line.width + strokeWidth;
if (strokeWidth <= 0)
strokeWidth = 1;
return new Line(line, style, src, stroke, strokeWidth, strokeDasharray,
strokeLinecap, level, fixed, fade, blur, isOutline);
}
if (!isOutline)
validate(strokeWidth);
return new Line(style, src, stroke, strokeWidth, strokeDasharray, strokeLinecap,
level, fixed, fade, blur, isOutline);
}
private static void validate(float strokeWidth) {
if (strokeWidth < 0) {
throw new IllegalArgumentException("stroke-width must not be negative: "
throw new IllegalArgumentException("width must not be negative: "
+ strokeWidth);
}
}
@ -107,15 +127,15 @@ public final class Line implements RenderInstruction {
/**
*
*/
public final int level;
private final int level;
/**
*
*/
public final Paint paint;
// public final Paint paint;
/**
*
*/
public final float strokeWidth;
public final float width;
/**
*
*/
@ -127,7 +147,7 @@ public final class Line implements RenderInstruction {
/**
*
*/
public final int outline;
public final boolean outline;
/**
*
@ -136,60 +156,83 @@ public final class Line implements RenderInstruction {
public final int fade;
private Line(String src, int stroke, float strokeWidth, float[] strokeDasharray,
Cap strokeLinecap, int level,
int outline, boolean fixed, int fade)
throws IOException {
public final String style;
public final Cap cap;
public final float blur;
private Line(String style, String src, int stroke, float strokeWidth,
float[] strokeDasharray, Cap strokeLinecap, int level, boolean fixed,
int fade, float blur, boolean isOutline) {
super();
Shader shader = BitmapUtils.createBitmapShader(src);
this.style = style;
// paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//
// if (src != null) {
// Shader shader = BitmapUtils.createBitmapShader(src);
// paint.setShader(shader);
// }
//
// paint.setStyle(Style.STROKE);
// paint.setColor(stroke);
// if (strokeDasharray != null) {
// paint.setPathEffect(new DashPathEffect(strokeDasharray, 0));
// }
// paint.setStrokeCap(strokeLinecap);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(shader);
paint.setStyle(Style.STROKE);
paint.setColor(stroke);
if (strokeDasharray != null) {
paint.setPathEffect(new DashPathEffect(strokeDasharray, 0));
}
paint.setStrokeCap(strokeLinecap);
round = (strokeLinecap == Cap.ROUND);
this.cap = strokeLinecap;
color = new float[4];
color[0] = (stroke >> 16 & 0xff) / 255.0f;
color[1] = (stroke >> 8 & 0xff) / 255.0f;
color[2] = (stroke >> 0 & 0xff) / 255.0f;
color[3] = (stroke >> 24 & 0xff) / 255.0f;
this.strokeWidth = strokeWidth;
this.width = strokeWidth;
this.level = level;
this.outline = outline;
this.outline = isOutline;
this.fixed = fixed;
this.blur = blur;
this.fade = fade;
}
@Override
public void destroy() {
// do nothing
}
private Line(Line line, String style, String src, int stroke, float strokeWidth,
float[] strokeDasharray, Cap strokeLinecap, int level, boolean fixed,
int fade, float blur, boolean isOutline) {
super();
@Override
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
// do nothing
this.style = style;
round = (strokeLinecap == Cap.ROUND);
color = line.color;
this.width = strokeWidth;
this.level = level;
this.outline = isOutline;
this.fixed = fixed;
this.fade = fade;
this.cap = strokeLinecap;
this.blur = blur;
}
@Override
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
// renderCallback.renderWay(mPaint, mLevel, mColor, mStrokeWidth, mRound, mOutline);
renderCallback.renderWay(this);
renderCallback.renderWay(this, level);
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
paint.setStrokeWidth(strokeWidth * scaleFactor);
}
// @Override
// public void scaleStrokeWidth(float scaleFactor) {
// paint.setStrokeWidth(strokeWidth * scaleFactor);
// }
@Override
public void scaleTextSize(float scaleFactor) {
// do nothing
public int getLevel() {
return this.level;
}
}

View File

@ -26,7 +26,7 @@ import android.graphics.Bitmap;
/**
* Represents an icon along a polyline on the map.
*/
public final class LineSymbol implements RenderInstruction {
public final class LineSymbol extends RenderInstruction {
/**
* @param elementName
* the name of the XML element.
@ -36,7 +36,8 @@ public final class LineSymbol implements RenderInstruction {
* @throws IOException
* if an I/O error occurs while reading a resource.
*/
public static LineSymbol create(String elementName, Attributes attributes) throws IOException {
public static LineSymbol create(String elementName, Attributes attributes)
throws IOException {
String src = null;
boolean alignCenter = false;
boolean repeat = false;
@ -62,7 +63,8 @@ public final class LineSymbol implements RenderInstruction {
private static void validate(String elementName, String src) {
if (src == null) {
throw new IllegalArgumentException("missing attribute src for element: " + elementName);
throw new IllegalArgumentException("missing attribute src for element: "
+ elementName);
}
}
@ -70,7 +72,8 @@ public final class LineSymbol implements RenderInstruction {
private final Bitmap mBitmap;
private final boolean mRepeat;
private LineSymbol(String src, boolean alignCenter, boolean repeat) throws IOException {
private LineSymbol(String src, boolean alignCenter, boolean repeat)
throws IOException {
super();
mBitmap = BitmapUtils.createBitmap(src);
@ -83,23 +86,8 @@ public final class LineSymbol implements RenderInstruction {
mBitmap.recycle();
}
@Override
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
// do nothing
}
@Override
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
renderCallback.renderWaySymbol(mBitmap, mAlignCenter, mRepeat);
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
// do nothing
}
@Override
public void scaleTextSize(float scaleFactor) {
// do nothing
}
}

View File

@ -24,13 +24,15 @@ import org.xml.sax.Attributes;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.graphics.Typeface;
import android.util.FloatMath;
/**
* Represents a text along a polyline on the map.
*/
public final class PathText implements RenderInstruction {
public final class PathText extends RenderInstruction {
/**
* @param elementName
* the name of the XML element.
@ -46,12 +48,14 @@ public final class PathText implements RenderInstruction {
int fill = Color.BLACK;
int stroke = Color.BLACK;
float strokeWidth = 0;
String style = null;
for (int i = 0; i < attributes.getLength(); ++i) {
String name = attributes.getLocalName(i);
String value = attributes.getValue(i);
if ("k".equals(name)) {
if ("name".equals(name))
style = value;
else if ("k".equals(name)) {
textKey = TextKey.getInstance(value);
} else if ("font-family".equals(name)) {
fontFamily = FontFamily.valueOf(value.toUpperCase(Locale.ENGLISH));
@ -72,67 +76,69 @@ public final class PathText implements RenderInstruction {
validate(elementName, textKey, fontSize, strokeWidth);
Typeface typeface = Typeface.create(fontFamily.toTypeface(), fontStyle.toInt());
return new PathText(textKey, typeface, fontSize, fill, stroke, strokeWidth);
return new PathText(style, textKey, typeface, fontSize, fill, stroke, strokeWidth);
}
private static void validate(String elementName, String textKey, float fontSize, float strokeWidth) {
private static void validate(String elementName, String textKey, float fontSize,
float strokeWidth) {
if (textKey == null) {
throw new IllegalArgumentException("missing attribute k for element: " + elementName);
throw new IllegalArgumentException("missing attribute k for element: "
+ elementName);
} else if (fontSize < 0) {
throw new IllegalArgumentException("font-size must not be negative: " + fontSize);
throw new IllegalArgumentException("font-size must not be negative: "
+ fontSize);
} else if (strokeWidth < 0) {
throw new IllegalArgumentException("stroke-width must not be negative: " + strokeWidth);
throw new IllegalArgumentException("stroke-width must not be negative: "
+ strokeWidth);
}
}
private final float mFontSize;
private final Paint mPaint;
private final Paint mStroke;
private final String mTextKey;
public final float fontSize;
public final Paint paint;
public Paint stroke;
public String textKey;
public final float fontHeight;
public final float fontDescent;
public String style;
private PathText(String textKey, Typeface typeface, float fontSize, int fill, int stroke, float strokeWidth) {
private PathText(String style, String textKey, Typeface typeface, float fontSize,
int fill, int outline, float strokeWidth) {
super();
mTextKey = textKey;
this.style = style;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextAlign(Align.CENTER);
mPaint.setTypeface(typeface);
mPaint.setColor(fill);
this.textKey = textKey;
mStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
mStroke.setStyle(Style.STROKE);
mStroke.setTextAlign(Align.CENTER);
mStroke.setTypeface(typeface);
mStroke.setColor(stroke);
mStroke.setStrokeWidth(strokeWidth);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextAlign(Align.CENTER);
paint.setTypeface(typeface);
paint.setColor(fill);
mFontSize = fontSize;
}
stroke = new Paint(Paint.ANTI_ALIAS_FLAG);
stroke.setStyle(Style.STROKE);
stroke.setTextAlign(Align.CENTER);
stroke.setTypeface(typeface);
stroke.setColor(outline);
stroke.setStrokeWidth(strokeWidth);
@Override
public void destroy() {
// do nothing
}
this.fontSize = fontSize;
@Override
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
// do nothing
paint.setTextSize(fontSize);
stroke.setTextSize(fontSize);
FontMetrics fm = paint.getFontMetrics();
fontHeight = FloatMath.ceil(Math.abs(fm.bottom) + Math.abs(fm.top));
fontDescent = FloatMath.ceil(Math.abs(fm.descent));
}
@Override
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
renderCallback.renderWayText(mTextKey, mPaint, mStroke);
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
// do nothing
renderCallback.renderWayText(this);
}
@Override
public void scaleTextSize(float scaleFactor) {
mPaint.setTextSize(mFontSize * scaleFactor);
mStroke.setTextSize(mFontSize * scaleFactor);
paint.setTextSize(fontSize * scaleFactor);
stroke.setTextSize(fontSize * scaleFactor);
}
}

View File

@ -20,11 +20,12 @@ import org.mapsforge.core.Tag;
/**
* A RenderInstruction is a basic graphical primitive to draw a map.
*/
public interface RenderInstruction {
public abstract class RenderInstruction {
/**
* Destroys this RenderInstruction and cleans up all its internal resources.
*/
void destroy();
public void destroy() {
}
/**
* @param renderCallback
@ -32,7 +33,8 @@ public interface RenderInstruction {
* @param tags
* the tags of the node.
*/
void renderNode(IRenderCallback renderCallback, Tag[] tags);
public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
}
/**
* @param renderCallback
@ -40,7 +42,8 @@ public interface RenderInstruction {
* @param tags
* the tags of the way.
*/
void renderWay(IRenderCallback renderCallback, Tag[] tags);
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
}
/**
* Scales the stroke width of this RenderInstruction by the given factor.
@ -48,7 +51,8 @@ public interface RenderInstruction {
* @param scaleFactor
* the factor by which the stroke width should be scaled.
*/
void scaleStrokeWidth(float scaleFactor);
public void scaleStrokeWidth(float scaleFactor) {
}
/**
* Scales the text size of this RenderInstruction by the given factor.
@ -56,5 +60,6 @@ public interface RenderInstruction {
* @param scaleFactor
* the factor by which the text size should be scaled.
*/
void scaleTextSize(float scaleFactor);
public void scaleTextSize(float scaleFactor) {
}
}

View File

@ -26,7 +26,7 @@ import android.graphics.Bitmap;
/**
* Represents an icon on the map.
*/
public final class Symbol implements RenderInstruction {
public final class Symbol extends RenderInstruction {
/**
* @param elementName
* the name of the XML element.
@ -36,7 +36,8 @@ public final class Symbol implements RenderInstruction {
* @throws IOException
* if an I/O error occurs while reading a resource.
*/
public static Symbol create(String elementName, Attributes attributes) throws IOException {
public static Symbol create(String elementName, Attributes attributes)
throws IOException {
String src = null;
for (int i = 0; i < attributes.getLength(); ++i) {
@ -56,7 +57,8 @@ public final class Symbol implements RenderInstruction {
private static void validate(String elementName, String src) {
if (src == null) {
throw new IllegalArgumentException("missing attribute src for element: " + elementName);
throw new IllegalArgumentException("missing attribute src for element: "
+ elementName);
}
}
@ -82,14 +84,4 @@ public final class Symbol implements RenderInstruction {
public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
renderCallback.renderAreaSymbol(mBitmap);
}
@Override
public void scaleStrokeWidth(float scaleFactor) {
// do nothing
}
@Override
public void scaleTextSize(float scaleFactor) {
// do nothing
}
}

View File

@ -24,6 +24,7 @@ import org.mapsforge.android.rendertheme.RenderTheme;
import org.mapsforge.android.rendertheme.renderinstruction.Area;
import org.mapsforge.android.rendertheme.renderinstruction.Caption;
import org.mapsforge.android.rendertheme.renderinstruction.Line;
import org.mapsforge.android.rendertheme.renderinstruction.PathText;
import org.mapsforge.core.Tag;
import org.mapsforge.core.Tile;
import org.mapsforge.database.IMapDatabase;
@ -414,24 +415,24 @@ public class MapGenerator implements IMapGenerator, IRenderCallback,
private List<ShapeContainer> mCurLevelContainer2;
@Override
public void renderWay(Line line) {
List<ShapeContainer> c = mDrawingLayer.add(line.level, mWayDataContainer,
line.paint);
if (mCurLevelContainer1 == null)
mCurLevelContainer1 = c;
else if (mCurLevelContainer2 == null)
mCurLevelContainer2 = c;
public void renderWay(Line line, int level) {
// List<ShapeContainer> c = mDrawingLayer.add(level, mWayDataContainer,
// line.paint);
//
// if (mCurLevelContainer1 == null)
// mCurLevelContainer1 = c;
// else if (mCurLevelContainer2 == null)
// mCurLevelContainer2 = c;
}
@Override
public void renderArea(Area area) {
if (area.paintFill != null)
mCurLevelContainer1 = mDrawingLayer.add(area.level, mWayDataContainer,
area.paintFill);
if (area.paintOutline != null)
mCurLevelContainer1 = mDrawingLayer.add(area.level, mWayDataContainer,
area.paintOutline);
public void renderArea(Area area, int level) {
// if (area.paintFill != null)
// mCurLevelContainer1 = mDrawingLayer.add(level, mWayDataContainer,
// area.paintFill);
// if (area.paintOutline != null)
// mCurLevelContainer1 = mDrawingLayer.add(level, mWayDataContainer,
// area.paintOutline);
}
@Override
@ -443,7 +444,7 @@ public class MapGenerator implements IMapGenerator, IRenderCallback,
}
@Override
public void renderWayText(String textKey, Paint paint, Paint outline) {
public void renderWayText(PathText pathText) {
// if (mWayDataContainer.textPos[0] >= 0)
// WayDecorator.renderText(this, paint, outline, mCoords, mWayDataContainer, mWayNames);
}

View File

@ -33,6 +33,7 @@ import org.mapsforge.android.mapgenerator.JobParameters;
import org.mapsforge.android.mapgenerator.MapGeneratorJob;
import org.mapsforge.android.mapgenerator.TileCacheKey;
import org.mapsforge.android.mapgenerator.TileDistanceSort;
import org.mapsforge.android.rendertheme.RenderTheme;
import org.mapsforge.android.utils.GlUtils;
import org.mapsforge.core.MapPosition;
import org.mapsforge.core.MercatorProjection;
@ -213,7 +214,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
newTiles[tiles++] = tile;
if (!tile.isReady || (tile.getScale() != scale)) {
tile.isLoading = true;
// tile.isLoading = true;
// approximation for TileScheduler
if (tileY < tileTop || tileY > tileBottom || tileX < tileLeft
|| tileX > tileRight)
@ -494,12 +495,6 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
}
}
}
// FIXME
// if (loadedTexture) {
// synchronized (mMapWorker) {
// mMapWorker.notify();
// }
// }
}
@Override
@ -588,4 +583,10 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
public IMapGenerator createMapGenerator() {
return new MapGenerator();
}
@Override
public void setRenderTheme(RenderTheme t) {
// TODO Auto-generated method stub
}
}

View File

@ -38,7 +38,8 @@ final class WayDecorator {
*/
private static final int SEGMENT_SAFETY_DISTANCE = 30;
static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter, boolean repeatSymbol, float[][] coordinates,
static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter,
boolean repeatSymbol, float[][] coordinates,
List<SymbolContainer> waySymbols) {
int skipPixels = SEGMENT_SAFETY_DISTANCE;
@ -68,9 +69,11 @@ final class WayDecorator {
// move the previous point forward towards the current point
previousX += diffX * segmentSkipPercentage;
previousY += diffY * segmentSkipPercentage;
symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY, currentX - previousX));
symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY,
currentX - previousX));
waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY, alignCenter, symbolAngle));
waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY,
alignCenter, symbolAngle));
// check if the symbol should only be rendered once
if (!repeatSymbol) {
@ -145,7 +148,8 @@ final class WayDecorator {
} else if ((currentY - nextY) == 0)
break;
float diff = ((float) (diffX) / (diffY) - (float) (currentX - nextX) / (currentY - nextY));
float diff = ((float) (diffX) / (diffY) - (float) (currentX - nextX)
/ (currentY - nextY));
// skip segments with corners
if (diff >= 0.2 || diff <= -0.2)
@ -210,7 +214,7 @@ final class WayDecorator {
y2 = previousY;
}
// estimate position of test on path
// estimate position of text on path
width = (x2 - x1) / 2;
x2 = x2 - (int) (width - s * width);
x1 = x1 + (int) (width - s * width);
@ -234,7 +238,8 @@ final class WayDecorator {
break;
// check crossings
if (GeometryUtils.lineIntersect(x1, y1, x2, y2, wtc2.x1, wtc2.y1, wtc2.x2, wtc2.y2)) {
if (GeometryUtils.lineIntersect(x1, y1, x2, y2, wtc2.x1, wtc2.y1,
wtc2.x2, wtc2.y2)) {
intersects = true;
break;
}
@ -244,7 +249,8 @@ final class WayDecorator {
short top2 = (wtc2.y1 < wtc2.y2 ? wtc2.y1 : wtc2.y2);
short bot2 = (wtc2.y1 < wtc2.y2 ? wtc2.y2 : wtc2.y1);
if (x1 - 10 < wtc2.x2 && wtc2.x1 - 10 < x2 && top - 10 < bot2 && top2 - 10 < bot) {
if (x1 - 10 < wtc2.x2 && wtc2.x1 - 10 < x2 && top - 10 < bot2
&& top2 - 10 < bot) {
if (wtc2.text.equals(text)) {
intersects = true;
@ -260,7 +266,8 @@ final class WayDecorator {
}
Log.d("mapsforge", "add " + text + " " + first + " " + last);
WayTextContainer wtc = new WayTextContainer(first, last, wayDataContainer, text,
WayTextContainer wtc = new WayTextContainer(first, last,
wayDataContainer, text,
paint);
wtc.x1 = (short) x1;
wtc.y1 = (short) y1;
@ -272,7 +279,8 @@ final class WayDecorator {
containerSize++;
if (outline != null) {
wayNames.add(new WayTextContainer(first, last, wayDataContainer, text, outline));
wayNames.add(new WayTextContainer(first, last, wayDataContainer,
text, outline));
containerSize++;
}
// 500 ??? how big is a tile?!

View File

@ -29,7 +29,7 @@ public class GlConfigChooser implements GLSurfaceView.EGLConfigChooser {
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_ALPHA_SIZE, 0,
EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_DEPTH_SIZE, 16,
// Requires that setEGLContextClientVersion(2) is called on the view.
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
// EGL10.EGL_SAMPLE_BUFFERS, 1 /* true */,
@ -50,7 +50,7 @@ public class GlConfigChooser implements GLSurfaceView.EGLConfigChooser {
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_ALPHA_SIZE, 0,
EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
EGL10.EGL_STENCIL_SIZE, 4,
EGL10.EGL_NONE };
@ -93,7 +93,10 @@ public class GlConfigChooser implements GLSurfaceView.EGLConfigChooser {
// // else
// if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 5
// &&
// findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 0) {
// findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 0
// // &&
// // findConfigAttrib(egl, display, configs[i], EGL10.EGL_DEPTH_SIZE, 0) == 16
// ) {
// index = i;
// break;
// }

View File

@ -92,9 +92,23 @@ public class Tag {
this.hashCodeValue = calculateHashCode();
}
/**
* @param key
* the key of the tag.
* @param value
* the value of the tag.
* @param intern
* true when string should be intern()alized.
*/
public Tag(String key, String value, boolean intern) {
this.key = (key == null ? null : key);
this.value = (value == null ? null : value);
if (intern) {
this.key = (key == null ? null : key.intern());
this.value = (value == null ? null : value.intern());
}
else {
this.key = (key == null ? null : key);
this.value = (value == null ? null : value);
}
this.hashCodeValue = calculateHashCode();
}

View File

@ -60,6 +60,8 @@ public class Tile {
*/
public final long pixelY;
public volatile boolean isCanceled;
/**
* @param tileX
* the X number of the tile.

View File

@ -251,7 +251,7 @@ public class MapDatabase implements IMapDatabase {
@Override
public String getMapProjection() {
return getMapFileInfo().projectionName;
return "WSG84"; // getMapFileInfo().projectionName;
}
/*
@ -883,14 +883,25 @@ public class MapDatabase implements IMapDatabase {
boolean featureWayDoubleDeltaEncoding = (featureByte & WAY_FEATURE_DOUBLE_DELTA_ENCODING) != 0;
// check if the way has a name
if ((featureByte & WAY_FEATURE_NAME) != 0)
if ((featureByte & WAY_FEATURE_NAME) != 0) {
textPos[0] = mReadBuffer.readUnsignedInt();
String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset
+ textPos[0]);
if (changed) {
Tag[] tmp = tags;
tags = new Tag[tmp.length + 1];
System.arraycopy(tmp, 0, tags, 0, tmp.length);
}
tags[tags.length - 1] = new Tag("name", str, false);
}
else
textPos[0] = -1;
// check if the way has a house number
if ((featureByte & WAY_FEATURE_HOUSE_NUMBER) != 0)
if ((featureByte & WAY_FEATURE_HOUSE_NUMBER) != 0) {
textPos[1] = mReadBuffer.readUnsignedInt();
}
else
textPos[1] = -1;

View File

@ -19,10 +19,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.zip.GZIPInputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@ -33,7 +33,6 @@ import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
@ -41,7 +40,6 @@ import org.mapsforge.core.BoundingBox;
import org.mapsforge.core.GeoPoint;
import org.mapsforge.core.Tag;
import org.mapsforge.core.Tile;
import org.mapsforge.core.WebMercator;
import org.mapsforge.database.FileOpenResult;
import org.mapsforge.database.IMapDatabase;
import org.mapsforge.database.IMapDatabaseCallback;
@ -60,45 +58,62 @@ public class MapDatabase implements IMapDatabase {
private static final MapFileInfo mMapInfo =
new MapFileInfo(new BoundingBox(-180, -90, 180, 90),
new Byte((byte) 4), new GeoPoint(53.11, 8.85),
WebMercator.NAME, 0, 0, 0, "de", "comment", "author");
null, 0, 0, 0, "de", "comment", "author");
private boolean mOpenFile = false;
// private static final String URL = "http://city.informatik.uni-bremen.de:8020/test/%d/%d/%d.osmtile";
private static final String URL = "http://city.informatik.uni-bremen.de/osmstache/test/%d/%d/%d.osmtile";
// private static final String URL = "http://city.informatik.uni-bremen.de/osmstache/gis2/%d/%d/%d.osmtile";
private static final Header encodingHeader =
new BasicHeader("Accept-Encoding", "gzip");
// private static final Header encodingHeader =
// new BasicHeader("Accept-Encoding", "gzip");
private static volatile HashMap<String, Tag> tagHash = new HashMap<String, Tag>(100);
private static final int MAX_TAGS_CACHE = 100;
private static Map<String, Tag> tagHash = Collections
.synchronizedMap(new LinkedHashMap<String, Tag>(
MAX_TAGS_CACHE, 0.75f, true) {
private Tag[] curTags = new Tag[250];
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(Entry<String, Tag> e) {
if (size() < MAX_TAGS_CACHE)
return false;
// Log.d(TAG, "cache: drop " + e.getValue());
return true;
}
});
private final static float REF_TILE_SIZE = 4096.0f;
private int MAX_TILE_TAGS = 100;
private Tag[] curTags = new Tag[MAX_TILE_TAGS];
private int mCurTagCnt;
private HttpClient mClient;
private IMapDatabaseCallback mMapGenerator;
private float mScaleFactor;
private HttpGet mRequest = null;
private Tile mTile;
@Override
public QueryResult executeQuery(Tile tile, IMapDatabaseCallback mapDatabaseCallback) {
mCanceled = false;
// just used for debugging ....
mTile = tile;
// Log.d(TAG, "get tile >> : " + tile);
String url = String.format(URL, Integer.valueOf(tile.zoomLevel),
Long.valueOf(tile.tileX), Long.valueOf(tile.tileY));
HttpGet getRequest = new HttpGet(url);
getRequest.addHeader(encodingHeader);
mRequest = getRequest;
mMapGenerator = mapDatabaseCallback;
mCurTagCnt = 0;
// using variable coordinate scalefactor to take advantage of varint
mScaleFactor = 1 / 100f;
if (tile.zoomLevel < 12)
mScaleFactor = (float) Math.pow(2, (12 - tile.zoomLevel)) / 100f;
mScaleFactor = REF_TILE_SIZE / Tile.TILE_SIZE;
try {
HttpResponse response = mClient.execute(getRequest);
@ -110,17 +125,24 @@ public class MapDatabase implements IMapDatabase {
entity.consumeContent();
return QueryResult.FAILED;
}
if (mTile.isCanceled) {
Log.d(TAG, "1 loading canceled " + mTile);
entity.consumeContent();
return QueryResult.FAILED;
}
InputStream is = null;
GZIPInputStream zis = null;
// GZIPInputStream zis = null;
try {
is = entity.getContent();
zis = new GZIPInputStream(is);
// zis = new GZIPInputStream(is);
decode(zis);
decode(is);
} finally {
if (zis != null)
zis.close();
// if (zis != null)
// zis.close();
if (is != null)
is.close();
entity.consumeContent();
@ -137,6 +159,12 @@ public class MapDatabase implements IMapDatabase {
return QueryResult.FAILED;
}
mRequest = null;
if (mTile.isCanceled) {
Log.d(TAG, "2 loading canceled " + mTile);
return QueryResult.FAILED;
}
// Log.d(TAG, "get tile << : " + tile);
return QueryResult.SUCCESS;
@ -144,7 +172,7 @@ public class MapDatabase implements IMapDatabase {
@Override
public String getMapProjection() {
return WebMercator.NAME;
return null;
}
@Override
@ -158,8 +186,6 @@ public class MapDatabase implements IMapDatabase {
}
private void createClient() {
// mClient = AndroidHttpClient.newInstance("Android");
mOpenFile = true;
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setStaleCheckingEnabled(params, false);
@ -193,7 +219,8 @@ public class MapDatabase implements IMapDatabase {
return null;
}
private static final int BUFFER_SIZE = 65536 * 2;
// // // hand sewed tile protocol buffers decoder // // //
private static final int BUFFER_SIZE = 65536;
private final byte[] buffer = new byte[BUFFER_SIZE];
private int bufferPos;
@ -261,20 +288,25 @@ public class MapDatabase implements IMapDatabase {
String tagString = decodeString();
Tag tag = tagHash.get(tagString);
if (tag == null) {
// Log.d(TAG, "tag:" + tagString);
if (tagString.equals("name="))
tag = new Tag(tagString, false);
if (tagString.startsWith(Tag.TAG_KEY_NAME))
tag = new Tag(Tag.TAG_KEY_NAME, tagString.substring(5), false);
else
tag = new Tag(tagString);
tagHash.put(tagString, tag);
}
// FIXME ...
if (mCurTagCnt < 250)
curTags[mCurTagCnt++] = tag;
//
// FIXME ...
if (mCurTagCnt >= MAX_TILE_TAGS) {
MAX_TILE_TAGS = mCurTagCnt + 10;
Tag[] tmp = new Tag[MAX_TILE_TAGS];
System.arraycopy(curTags, 0, tmp, 0, mCurTagCnt);
curTags = tmp;
}
curTags[mCurTagCnt++] = tag;
return true;
}
@ -285,7 +317,7 @@ public class MapDatabase implements IMapDatabase {
int indexCnt = 0;
int tagCnt = 0;
int coordCnt = 0;
int layer = 0;
int layer = 5;
Tag[] tags = null;
short[] index = null;
@ -294,7 +326,6 @@ public class MapDatabase implements IMapDatabase {
while (bytesRead < end) {
// read tag and wire type
int val = decodeVarint32();
if (val == 0)
break;
@ -350,18 +381,10 @@ public class MapDatabase implements IMapDatabase {
}
float[] coords = tmpCoords;
int pos = 0;
float z = mScaleFactor;
for (int j = 0, m = indexCnt; j < m; j++) {
float lastX = 0;
float lastY = 0;
for (int n = index[j] + pos; pos < n; pos += 2) {
lastX = coords[pos] = (coords[pos] * z) + lastX;
lastY = coords[pos + 1] = (coords[pos + 1] * z) + lastY;
}
}
// FIXME !!!!!
if (layer == 0)
layer = 5;
mMapGenerator.renderWay((byte) layer, tags, coords, index, polygon);
return true;
@ -429,12 +452,20 @@ public class MapDatabase implements IMapDatabase {
readBuffer(bytes);
int cnt = 0;
int end = bufferPos + bytes;
float scale = mScaleFactor;
// read repeated sint32
int lastX = 0;
int lastY = 0;
while (bufferPos < end && cnt < numNodes) {
float lon = decodeZigZag32(decodeVarint32()) * mScaleFactor;
float lat = decodeZigZag32(decodeVarint32()) * mScaleFactor;
mMapGenerator.renderPointOfInterest(layer, lat, lon, tags);
int lon = decodeZigZag32(decodeVarint32()); // * mScaleFactor;
int lat = decodeZigZag32(decodeVarint32()); // * mScaleFactor;
lastX = lon + lastX;
lastY = lat + lastY;
mMapGenerator.renderPointOfInterest(layer,
lastY / scale,
lastX / scale,
tags);
cnt += 2;
}
return cnt;
@ -443,13 +474,6 @@ public class MapDatabase implements IMapDatabase {
private int MAX_WAY_COORDS = 32768;
private float[] tmpCoords = new float[MAX_WAY_COORDS];
// private boolean ensureBufferSize(int size) throws IOException {
// if (size > (bufferSize - bufferPos))
// readBuffer(size - (bufferSize - bufferPos));
//
// return true;
// }
private Tag[] decodeWayTags(int tagCnt) throws IOException {
int bytes = decodeVarint32();
@ -457,33 +481,45 @@ public class MapDatabase implements IMapDatabase {
int cnt = 0;
int end = bytesRead + bytes;
int max = curTags.length;
int max = mCurTagCnt;
while (bytesRead < end) {
int tagNum = decodeVarint32();
if (tagNum < 0 || cnt == tagCnt)
continue;
if (tagNum < 0 || cnt == tagCnt) {
Log.d(TAG, "NULL TAG: " + mTile + " invalid tag:" + tagNum + " "
+ tagCnt + "/" + cnt);
break;
}
if (tagNum < Tags.MAX)
tags[cnt++] = Tags.tags[tagNum];
else {
tagNum -= Tags.LIMIT;
if (tagNum >= 0 && tagNum < max) {
// Log.d(TAG, "variable tag: " + curTags[tagNum]);
tags[cnt++] = curTags[tagNum];
} else {
Log.d(TAG, "NULL TAG: " + mTile + " could find tag:" + tagNum + " "
+ tagCnt + "/" + cnt);
}
}
// else DEBUG...
}
if (tagCnt != cnt)
Log.d(TAG, "NULL TAG: " + mTile + " ...");
return tags;
}
private short[] mIndices = new short[10];
private short[] decodeWayIndices(int indexCnt) throws IOException {
int bytes = decodeVarint32();
short[] index = new short[indexCnt];
short[] index = mIndices;
if (index.length < indexCnt + 1) {
index = mIndices = new short[indexCnt + 1];
}
int cnt = 0;
int end = bytesRead + bytes;
@ -495,6 +531,9 @@ public class MapDatabase implements IMapDatabase {
// else DEBUG...
}
index[indexCnt] = -1;
return index;
}
@ -514,67 +553,78 @@ public class MapDatabase implements IMapDatabase {
int cnt = 0;
int result;
int x, lastX = 0;
int y, lastY = 0;
boolean even = true;
float scale = mScaleFactor;
// read repeated sint32
while (pos < end) {
if (cnt >= MAX_WAY_COORDS) {
Log.d(TAG, "increase way coord buffer " + mTile);
MAX_WAY_COORDS += 128;
float[] tmp = new float[MAX_WAY_COORDS];
System.arraycopy(coords, 0, tmp, 0, cnt);
tmpCoords = coords = tmp;
}
byte tmp = buf[pos++];
if (tmp >= 0) {
result = tmp;
if (buf[pos] >= 0) {
result = buf[pos++];
} else if (buf[pos + 1] >= 0) {
result = buf[pos] & 0x7f
| buf[pos + 1] << 7;
pos += 2;
} else if (buf[pos + 2] >= 0) {
result = buf[pos] & 0x7f
| buf[pos + 1] << 7
| buf[pos + 2] << 14;
pos += 3;
} else if (buf[pos + 3] >= 0) {
result = buf[pos] & 0x7f
| buf[pos + 1] << 7
| buf[pos + 2] << 14
| buf[pos + 3] << 21;
pos += 4;
Log.d(TAG, "4 Stuffs too large " + mTile);
} else {
result = tmp & 0x7f;
if ((tmp = buf[pos++]) >= 0) {
result |= tmp << 7;
} else {
result |= (tmp & 0x7f) << 7;
if ((tmp = buf[pos++]) >= 0) {
result |= tmp << 14;
} else {
result |= (tmp & 0x7f) << 14;
if ((tmp = buf[pos++]) >= 0) {
result |= tmp << 21;
} else {
result |= (tmp & 0x7f) << 21;
result |= (tmp = buf[pos++]) << 28;
result = buf[pos] & 0x7f
| buf[pos + 1] << 7
| buf[pos + 2] << 14
| buf[pos + 3] << 21
| buf[pos + 4] << 28;
pos += 5;
if (tmp < 0) {
int i = 0;
// Discard upper 32 bits.
while (i++ < 5) {
if (buf[pos++] >= 0)
break;
}
Log.d(TAG, "5 Stuffs too large " + mTile);
if (i == 5)
// FIXME throw some poo
Log.d(TAG, "EEK malformedVarint");
}
}
if (buf[pos + 4] < 0) {
Log.d(TAG, "Stuffs too large ...");
int i = 0;
while (i++ < 5) {
if (buf[pos++] >= 0)
break;
}
if (i == 5)
throw new IOException("EEEK malformed varInt");
}
}
coords[cnt++] = (result >>> 1) ^ -(result & 1);
if (even) {
x = ((result >>> 1) ^ -(result & 1));
lastX = lastX + x;
coords[cnt++] = lastX / scale;
even = false;
} else {
y = ((result >>> 1) ^ -(result & 1));
lastY = lastY + y;
coords[cnt++] = lastY / scale;
even = true;
}
}
bufferPos = pos;
bytesRead += bytes;
// while (bytesRead < end) {
// int val = decodeZigZag32(decodeVarint32());
// if (cnt >= MAX_WAY_COORDS) {
// MAX_WAY_COORDS += 128;
// Log.d(TAG, "increase coords array " + MAX_WAY_COORDS);
// float[] tmp = new float[MAX_WAY_COORDS];
// System.arraycopy(tmpCoords, 0, tmp, 0, cnt);
// tmpCoords = tmp;
// }
// tmpCoords[cnt++] = val;
// }
return cnt;
}
@ -589,9 +639,6 @@ public class MapDatabase implements IMapDatabase {
}
bufferSize = len;
bufferPos = 0;
// Log.d(TAG, "pos " + bufferPos + ", size " + bufferSize + ", read "
// + bytesRead);
}
private void readBuffer(int size) throws IOException {
@ -612,6 +659,10 @@ public class MapDatabase implements IMapDatabase {
}
while ((bufferSize - bufferPos) < size) {
if (mTile.isCanceled) {
throw new IOException("canceled " + mTile);
}
// read until requested size is available in buffer
int len = inputStream.read(buffer, bufferSize, BUFFER_SIZE - bufferSize);
if (len < 0) {
@ -621,13 +672,6 @@ public class MapDatabase implements IMapDatabase {
bufferSize += len;
// if (len == 0)
// try {
// wait(50);
// } catch (InterruptedException e) {
// // just waiting for data
// }
if (mCanceled)
throw new IOException("... canceld?");
}
@ -637,6 +681,17 @@ public class MapDatabase implements IMapDatabase {
// + ", read " + bytesRead);
}
private boolean mCanceled;
@Override
public void cancel() {
mCanceled = true;
if (mRequest != null) {
mRequest.abort();
mRequest = null;
}
}
/* All code below is taken from or based on Google's Protocol Buffers implementation: */
// Protocol Buffers - Google's data interchange format
@ -727,17 +782,4 @@ public class MapDatabase implements IMapDatabase {
return (n >>> 1) ^ -(n & 1);
}
private boolean mCanceled;
@Override
public void cancel() {
mCanceled = true;
if (mRequest != null) {
mRequest.abort();
mRequest = null;
}
// mClient.getConnectionManager().shutdown();
// mClient = null;
}
}

View File

@ -45,14 +45,14 @@ import android.util.Log;
public class MapDatabase implements IMapDatabase {
private final static String TAG = "MapDatabase";
private static final String QUERY = "SELECT tags, geom FROM __get_tile(?,?,?)";
private static final String QUERY = "SELECT tags, geom FROM __get_tile(?,?,?,false)";
private final float mScale = 1; // 1000000.0f;
private final float mScale = 1;
private int mCoordPos = 0;
private int mIndexPos = 0;
private float[] mCoords = new float[100000];
private short[] mIndex = new short[10000];
private float[] mCoords;
private short[] mIndex;
private Tag[] mTags;
@ -71,7 +71,7 @@ public class MapDatabase implements IMapDatabase {
private boolean connect() {
Connection conn = null;
String dburl = "jdbc:postgresql://city.informatik.uni-bremen.de:5432/gis-2.0";
String dburl = "jdbc:postgresql://city.informatik.uni-bremen.de:5432/gis";
Properties dbOpts = new Properties();
dbOpts.setProperty("user", "osm");
@ -212,6 +212,10 @@ public class MapDatabase implements IMapDatabase {
@Override
public FileOpenResult openFile(File mapFile) {
mOpenFile = true;
if (mCoords == null) {
mCoords = new float[100000];
mIndex = new short[100000];
}
return new FileOpenResult();
}
@ -226,6 +230,8 @@ public class MapDatabase implements IMapDatabase {
connection = null;
}
}
mCoords = null;
mIndex = null;
mOpenFile = false;
}