- rename MapRenderer to TileManager and refactor

- move overlay renderer to own package
- slight TouchHandler improvements, not start rotation when scaling
This commit is contained in:
Hannes Janetzek
2012-10-20 00:50:32 +02:00
parent 98246fe50b
commit 78aac5f019
20 changed files with 243 additions and 160 deletions

View File

@@ -0,0 +1,158 @@
/*
* 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
* 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.oscim.renderer.overlays;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.LineRenderer;
import org.oscim.renderer.PolygonRenderer;
import org.oscim.renderer.TextureRenderer;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers;
import org.oscim.utils.FastMath;
import org.oscim.view.MapView;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;
public abstract class Overlay {
protected final MapView mMapView;
protected MapPosition mMapPosition;
public final Layers layers;
// flag to set when data is ready for (re)compilation.
public boolean newData;
// flag set by GLRenderer when data is compiled
public boolean isReady;
public BufferObject vbo;
public Overlay(MapView mapView) {
mMapView = mapView;
mMapPosition = new MapPosition();
layers = new Layers();
}
synchronized boolean onTouch(boolean down) {
Log.d("...", "Overlay handle onTouch " + down);
return true;
}
/**
* update mMapPosition
*
* @return true if position has changed
*/
protected boolean updateMapPosition() {
return mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
}
// /////////////// called from GLRender Thread ////////////////////////
// use synchronized (this){} when updating 'layers' from another thread
/**
* @param positionChanged
* true when MapPosition has changed
* @param tilesChanged
* true when loaded tiles changed
*/
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
// // keep position constant (or update layer relative to new position)
// mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
//
// if (first) {
// // fix at initial position
// // mapView.getMapViewPosition().getMapPosition(mMapPosition, null);
// first = false;
//
// // pass layers to be uploaded and drawn to GL Thread
// // afterwards never modify 'layers' outside of this function!
// newData = true;
// }
}
float[] mvp = new float[16];
public synchronized void render(MapPosition pos, float[] mv, float[] proj) {
float div = setMatrix(pos, mv);
Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo.id);
for (Layer l = layers.layers; l != null;) {
if (l.type == Layer.POLYGON) {
GLES20.glDisable(GLES20.GL_BLEND);
l = PolygonRenderer.draw(pos, l, mvp, true, false);
} else {
GLES20.glEnable(GLES20.GL_BLEND);
l = LineRenderer.draw(pos, l, mvp, 1 / div, 0, layers.lineOffset);
}
}
// float scale = curPos.scale / div;
for (Layer l = layers.textureLayers; l != null;) {
l = TextureRenderer.draw(l, (mMapPosition.scale / pos.scale) * div, proj, mv,
layers.texOffset);
}
}
private float setMatrix(MapPosition curPos, float[] matrix) {
MapPosition oPos = mMapPosition;
byte z = oPos.zoomLevel;
// int diff = curPos.zoomLevel - z;
float div = FastMath.pow(z - curPos.zoomLevel);
// if (diff < 0)
// div = (1 << -diff);
// else if (diff > 0)
// div = (1.0f / (1 << diff));
float x = (float) (oPos.x - curPos.x * div);
float y = (float) (oPos.y - curPos.y * div);
// flip around date-line
float max = (Tile.TILE_SIZE << z);
if (x < -max / 2)
x = max + x;
else if (x > max / 2)
x = x - max;
float scale = curPos.scale / div;
Matrix.setIdentityM(matrix, 0);
// translate relative to map center
matrix[12] = x * scale;
matrix[13] = y * scale;
scale = (curPos.scale / oPos.scale) / div;
// scale to tile to world coordinates
scale /= GLRenderer.COORD_MULTIPLIER;
matrix[0] = scale;
matrix[5] = scale;
Matrix.multiplyMM(matrix, 0, curPos.viewMatrix, 0, matrix, 0);
return div;
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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
* 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.oscim.renderer.overlays;
import org.oscim.core.Tile;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.TextItem;
import org.oscim.renderer.layer.TextLayer;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.view.MapView;
import android.graphics.Color;
import android.graphics.Paint.Cap;
import android.util.Log;
public class OverlayGrid extends Overlay {
private float[] mPoints;
private short[] mIndex;
private Text mText;
OverlayGrid(MapView mapView) {
super(mapView);
int size = Tile.TILE_SIZE;
float[] points = new float[64];
short[] index = new short[16];
float pos = -size * 4;
// vertical lines
for (int i = 0; i < 8; i++) {
index[i] = 4;
// x1,y1,x2,y2
points[i * 4] = pos + i * size;
points[i * 4 + 1] = pos + 0;
points[i * 4 + 2] = pos + i * size;
points[i * 4 + 3] = pos + size * 8;
}
// horizontal lines
for (int j = 8; j < 16; j++) {
index[j] = 4;
points[j * 4] = pos + 0;
points[j * 4 + 1] = pos + (j - 8) * size;
points[j * 4 + 2] = pos + size * 8;
points[j * 4 + 3] = pos + (j - 8) * size;
}
mIndex = index;
mPoints = points;
// mText = Text.createText(20, 3, Color.BLACK, Color.RED, false);
mText = Text.createText(22, 0, Color.RED, 0, false);
// mText = Text.createText(22, 0, Color.RED, 0, true);
}
private void addLabels(int x, int y, int z) {
int size = Tile.TILE_SIZE;
TextLayer tl = new TextLayer();
for (int i = -2; i < 2; i++) {
for (int j = -2; j < 2; j++) {
TextItem ti = TextItem.get().set(size * j + size / 2, size * i + size / 2,
(x + j) + " / " + (y + i) + " / " + z, mText);
// TextItem ti = new TextItem(size * j + size / 2, size * i +
// size / 2,
// (x + j) + " / " + (y + i) + " / " + z, mText);
// rotation, TODO could also be used for slide range
ti.x1 = 0;
ti.y1 = 1; // (short) (size / 2);
ti.x2 = 1; // (short) size;
ti.y2 = 1; // (short) (size / 2);
tl.addText(ti);
}
}
layers.textureLayers = tl;
}
private int mCurX = -1;
private int mCurY = -1;
private byte mCurZ = -1;
private boolean finished;
void timerFinished() {
Log.d("...", "timer finish!");
finished = true;
mMapView.redrawMap();
}
@Override
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
updateMapPosition();
// fix map position to tile coordinates
float size = Tile.TILE_SIZE;
int x = (int) (mMapPosition.x / size);
int y = (int) (mMapPosition.y / size);
mMapPosition.x = x * size;
mMapPosition.y = y * size;
if (!finished)
mMapPosition.scale = 1;
// update layers when map moved by at least one tile
if (x != mCurX || y != mCurY || mMapPosition.zoomLevel != mCurZ) {
mCurX = x;
mCurY = y;
mCurZ = mMapPosition.zoomLevel;
layers.clear();
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
ll.width = 1.5f;
ll.addLine(mPoints, mIndex, false);
addLabels(x, y, mCurZ);
newData = true;
finished = false;
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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
* 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.oscim.renderer.overlays;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.view.MapView;
import android.graphics.Color;
import android.graphics.Paint.Cap;
public class OverlayTest extends Overlay {
TextItem labels;
float drawScale;
private boolean first = true;
OverlayTest(MapView mapView) {
super(mapView);
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
ll.width = 2;
float[] points = { -100, -100, 100, -100, 100, 100, -100, 100, -100, -100 };
short[] index = { (short) points.length };
ll.addLine(points, index, false);
//
// PolygonLayer pl = (PolygonLayer) layers.getLayer(0, Layer.POLYGON);
// pl.area = new Area(Color.argb(128, 255, 0, 0));
//
// float[] ppoints = {
// 0, 256,
// 0, 0,
// 256, 0,
// 256, 256,
// };
// short[] pindex = { (short) ppoints.length };
// pl.addPolygon(ppoints, pindex);
// SymbolLayer sl = new SymbolLayer();
// SymbolItem it = new SymbolItem();
//
// it.x = 0;
// it.y = 0;
// // billboard always faces camera
// it.billboard = true;
//
// try {
// it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png");
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// sl.addSymbol(it);
//
// SymbolItem it2 = new SymbolItem();
// it2.bitmap = it.bitmap;
// it2.x = 0;
// it2.y = 0;
// // billboard always faces camera
// it2.billboard = false;
//
// sl.addSymbol(it2);
// TextLayer tl = new TextLayer();
// Text t = Text.createText(20, 2, Color.WHITE, Color.BLACK, false);
// TextItem ti = new TextItem(0, 0, "check one, check two", t);
// ti.x1 = 0;
// ti.y1 = 0;
// ti.x2 = (short) Tile.TILE_SIZE;
// ti.y2 = (short) Tile.TILE_SIZE;
//
// tl.addText(ti);
//
// layers.textureLayers = tl;
}
@Override
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
// keep position constant (or update layer relative to new position)
mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
if (first) {
// fix at initial position
// mapView.getMapViewPosition().getMapPosition(mMapPosition, null);
first = false;
// pass layers to be uploaded and drawn to GL Thread
// afterwards never modify 'layers' outside of this function!
newData = true;
}
}
}

View File

@@ -0,0 +1,166 @@
/*
* 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
* 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.oscim.renderer.overlays;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.renderer.TileManager;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.Tiles;
import org.oscim.renderer.layer.TextItem;
import org.oscim.renderer.layer.TextLayer;
import org.oscim.utils.FastMath;
import org.oscim.utils.PausableThread;
import org.oscim.view.MapView;
import android.os.SystemClock;
import android.util.FloatMath;
public class OverlayText extends Overlay {
private Tiles tiles;
private LabelThread mThread;
/* package */boolean mRun;
/* package */boolean mRerun;
private MapPosition mWorkPos;
private TextLayer mWorkLayer;
class LabelThread extends PausableThread {
@Override
protected void doWork() {
SystemClock.sleep(250);
mRun = false;
updateLabels();
mMapView.redrawMap();
}
@Override
protected String getThreadName() {
return "Labeling";
}
@Override
protected boolean hasWork() {
return mRun || mRerun;
}
}
public OverlayText(MapView mapView) {
super(mapView);
mWorkPos = new MapPosition();
mThread = new LabelThread();
mThread.start();
}
void updateLabels() {
tiles = TileManager.getActiveTiles(tiles);
if (tiles.cnt == 0)
return;
mMapView.getMapViewPosition().getMapPosition(mWorkPos, null);
// TODO tiles might be from another zoomlevel than the current:
// this scales MapPosition to the zoomlevel of tiles...
int diff = tiles.tiles[0].zoomLevel - mWorkPos.zoomLevel;
float div = FastMath.pow(diff);
// fix map position to tile coordinates
float size = Tile.TILE_SIZE;
int x = (int) (mWorkPos.x / div / size);
int y = (int) (mWorkPos.y / div / size);
mWorkPos.x = x * size;
mWorkPos.y = y * size;
mWorkPos.zoomLevel += diff;
mWorkPos.scale = div;
// Log.d("...", "relabel " + mRerun + " " + x + " " + y);
TextLayer tl = new TextLayer();
float angle = (float) Math.toRadians(mWorkPos.angle);
float cos = FloatMath.cos(angle);
float sin = FloatMath.sin(angle);
for (int i = 0, n = tiles.cnt; i < n; i++) {
MapTile t = tiles.tiles[i];
if (!t.isVisible)
continue;
int dx = (t.tileX - x) * Tile.TILE_SIZE;
int dy = (t.tileY - y) * Tile.TILE_SIZE;
// Log.d("...", "update tiles " + dx + " " + dy);
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
TextItem ti2 = TextItem.get().move(ti, dx, dy);
if (!ti.text.caption) {
if (cos * (ti.x2 - ti.x1) - sin * (ti.y2 - ti.y1) < 0) {
// flip label upside-down
ti2.x1 = ti.x2;
ti2.y1 = ti.y2;
ti2.x2 = ti.x1;
ti2.y2 = ti.y1;
} else {
ti2.x1 = ti.x1;
ti2.y1 = ti.y1;
ti2.x2 = ti.x2;
ti2.y2 = ti.y2;
}
}
tl.addText(ti2);
}
}
// everything synchronized?
synchronized (this) {
mWorkLayer = tl;
}
}
@Override
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
// Log.d("...", "update " + tilesChanged + " " + positionChanged);
if (mWorkLayer != null) {
layers.clear();
layers.textureLayers = mWorkLayer;
mWorkLayer = null;
// make the 'labeled' MapPosition current
MapPosition tmp = mMapPosition;
mMapPosition = mWorkPos;
mWorkPos = tmp;
// TODO should return true instead
newData = true;
}
if (tilesChanged || positionChanged) {
if (!mRun) {
mRun = true;
synchronized (mThread) {
mThread.notify();
}
}
}
}
}