MapView base layer is now a TileLayer

- extract MapView base layer into TileLayer
- extract MapTile loading from GLRenderer
- move all tile layer related classes to layers.tile.*
- make Overlay subclass of InputLayer, which extends Layer
This commit is contained in:
Hannes Janetzek 2013-04-21 23:20:23 +02:00
parent 1c779f2f60
commit 6eb3b9221b
32 changed files with 1398 additions and 1340 deletions

View File

@ -15,7 +15,7 @@
*/
package org.oscim.database;
import org.oscim.generator.JobTile;
import org.oscim.layers.tile.JobTile;
/**
*

View File

@ -27,7 +27,7 @@ import org.oscim.database.MapOptions;
import org.oscim.database.mapfile.header.MapFileHeader;
import org.oscim.database.mapfile.header.MapFileInfo;
import org.oscim.database.mapfile.header.SubFileParameter;
import org.oscim.generator.JobTile;
import org.oscim.layers.tile.JobTile;
import android.util.Log;

View File

@ -30,7 +30,7 @@ import org.oscim.database.IMapDatabase;
import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo;
import org.oscim.database.MapOptions;
import org.oscim.generator.JobTile;
import org.oscim.layers.tile.JobTile;
import android.os.Environment;
import android.os.SystemClock;

View File

@ -43,7 +43,7 @@ import org.oscim.database.IMapDatabase;
import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo;
import org.oscim.database.MapOptions;
import org.oscim.generator.JobTile;
import org.oscim.layers.tile.JobTile;
import android.os.Environment;
import android.os.SystemClock;

View File

@ -26,7 +26,7 @@ import org.oscim.database.IMapDatabase;
import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo;
import org.oscim.database.MapOptions;
import org.oscim.generator.JobTile;
import org.oscim.layers.tile.JobTile;
/**
*

View File

@ -0,0 +1,228 @@
/*
* Copyright 2012 osmdroid authors
* Copyright 2013 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.layers;
import org.oscim.view.MapView;
import android.view.KeyEvent;
import android.view.MotionEvent;
public abstract class InputLayer extends Layer {
public InputLayer(MapView mapView) {
super(mapView);
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param keyCode
* ...
* @param event
* ...
* @return ...
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param keyCode
* ...
* @param event
* ...
* @return ...
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
/**
* <b>You can prevent all(!) other Touch-related events from happening!</b><br />
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onTouchEvent(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onTrackballEvent(MotionEvent e) {
return false;
}
/** GestureDetector.OnDoubleTapListener **/
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onDoubleTap(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
/** OnGestureListener **/
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onDown(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param pEvent1
* ...
* @param pEvent2
* ...
* @param pVelocityX
* ...
* @param pVelocityY
* ...
* @return ...
*/
public boolean onFling(MotionEvent pEvent1, MotionEvent pEvent2,
float pVelocityX, float pVelocityY) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onLongPress(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param pEvent1
* ...
* @param pEvent2
* ...
* @param pDistanceX
* ...
* @param pDistanceY
* ...
* @return ...
*/
public boolean onScroll(MotionEvent pEvent1, MotionEvent pEvent2,
float pDistanceX, float pDistanceY) {
return false;
}
/**
* @param pEvent
* ...
*/
public void onShowPress(MotionEvent pEvent) {
return;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2013 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.layers;
import org.oscim.core.MapPosition;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.view.MapView;
public class Layer {
public Layer(MapView mapView) {
mMapView = mapView;
}
private boolean mEnabled = true;
protected final MapView mMapView;
/** RenderOverlay used to draw this layer. To be implemented by sub-classes */
protected RenderOverlay mLayer;
public RenderOverlay getLayer() {
return mLayer;
}
/**
*/
public void setEnabled(boolean pEnabled) {
mEnabled = pEnabled;
}
/**
*/
public boolean isEnabled() {
return mEnabled;
}
/**
* Called before each frame render request (on main thread).
*
* @param mapPosition
* current MapPosition
* @param changed
* true when MapPosition has changed since last call
*/
public void onUpdate(MapPosition mapPosition, boolean changed) {
}
/**
* Override to perform clean up of resources before shutdown. By default
* does nothing.
*/
public void onDetach() {
// FIXME call to this function is not implemented
}
public void destroy() {
// TODO Auto-generated method stub
}
}

View File

@ -13,10 +13,10 @@
* 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.generator;
package org.oscim.layers.tile;
import static org.oscim.generator.JobTile.STATE_LOADING;
import static org.oscim.generator.JobTile.STATE_NONE;
import static org.oscim.layers.tile.JobTile.STATE_LOADING;
import static org.oscim.layers.tile.JobTile.STATE_NONE;
/**
* A JobQueue keeps the list of pending jobs for a MapView and prioritizes them.

View File

@ -12,7 +12,7 @@
* 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.generator;
package org.oscim.layers.tile;
import org.oscim.core.Tile;

View File

@ -12,11 +12,11 @@
* You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
package org.oscim.layers.tile;
import org.oscim.generator.JobTile;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.TextItem;
import org.oscim.utils.quadtree.QuadTree;
/**
* Extends Tile class for concurrent use in TileManager,
@ -41,7 +41,7 @@ public final class MapTile extends JobTile {
/**
* Pointer to access relatives in QuadTree
*/
public QuadTree rel;
public QuadTree<MapTile> rel;
int lastDraw = 0;
@ -92,26 +92,24 @@ public final class MapTile extends JobTile {
return;
// lock all tiles that could serve as proxy
MapTile p = rel.parent.tile;
MapTile p = rel.parent.item;
if (p != null && (p.state != 0)) {
proxies |= PROXY_PARENT;
p.refs++;
}
p = rel.parent.parent.tile;
p = rel.parent.parent.item;
if (p != null && (p.state != 0)) {
proxies |= PROXY_GRAMPA;
p.refs++;
}
for (int j = 0; j < 4; j++) {
if (rel.child[j] != null) {
p = rel.child[j].tile;
if (p != null && (p.state != 0)) {
proxies |= (1 << j);
p.refs++;
}
}
if ((p = rel.get(j)) == null || p.state == 0)
continue;
proxies |= (1 << j);
p.refs++;
}
}
@ -120,14 +118,14 @@ public final class MapTile extends JobTile {
return;
if ((proxies & PROXY_PARENT) != 0)
rel.parent.tile.refs--;
rel.parent.item.refs--;
if ((proxies & PROXY_GRAMPA) != 0)
rel.parent.parent.tile.refs--;
rel.parent.parent.item.refs--;
for (int i = 0; i < 4; i++) {
if ((proxies & (1 << i)) != 0)
rel.child[i].tile.refs--;
rel.get(i).refs--;
}
proxies = 0;
}

View File

@ -1,6 +1,6 @@
/*
* Copyright 2010, 2011, 2012 mapsforge.org
*
* Copyright 2013 Hannes 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.
@ -12,9 +12,8 @@
* 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.generator;
package org.oscim.layers.tile;
import org.oscim.renderer.TileManager;
import org.oscim.utils.PausableThread;
/**
@ -39,6 +38,7 @@ public class MapWorker extends PausableThread {
*/
public MapWorker(int id, JobQueue jobQueue, TileGenerator tileGenerator,
TileManager tileManager) {
super();
mJobQueue = jobQueue;
mMapGenerator = tileGenerator;

View File

@ -12,7 +12,7 @@
* 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.generator;
package org.oscim.layers.tile;
/**
* Sort Tiles by 'distance' value.

View File

@ -12,11 +12,12 @@
* 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.generator;
package org.oscim.layers.tile;
import static org.oscim.core.MapElement.GEOM_LINE;
import static org.oscim.core.MapElement.GEOM_POINT;
import static org.oscim.core.MapElement.GEOM_POLY;
import static org.oscim.generator.JobTile.STATE_NONE;
import static org.oscim.layers.tile.JobTile.STATE_NONE;
import java.util.Arrays;
@ -25,9 +26,8 @@ import org.oscim.core.MercatorProjection;
import org.oscim.core.Tag;
import org.oscim.core.Tile;
import org.oscim.database.IMapDatabase;
import org.oscim.database.IMapDatabase.QueryResult;
import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.QueryResult;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.layer.ExtrusionLayer;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer;
@ -71,12 +71,18 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private static final Tag[] debugTagWay = { new Tag("debug", "way") };
private static final Tag[] debugTagArea = { new Tag("debug", "area") };
// replacement for variable value tags that should not be matched by RenderTheme
// FIXME make this general, maybe subclass tags
private static final Tag mTagEmptyName = new Tag(Tag.TAG_KEY_NAME, null, false);
private static final Tag mTagEmptyHouseNr = new Tag(Tag.TAG_KEY_HOUSE_NUMBER, null, false);
private final MapElement mDebugWay, mDebugPoint;
private static RenderTheme renderTheme;
private static int renderLevels;
private static DebugSettings debug;
private RenderTheme renderTheme;
private int renderLevels;
// current MapDatabase used by this TileGenerator
private IMapDatabase mMapDatabase;
@ -95,16 +101,12 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private float mLatScaleFactor;
private float mGroundResolution;
// replacement for variable value tags that should not be matched by RenderTheme
// FIXME make this general, maybe subclass tags
private final static Tag mTagEmptyName = new Tag(Tag.TAG_KEY_NAME, null, false);
private final static Tag mTagEmptyHouseNr = new Tag(Tag.TAG_KEY_HOUSE_NUMBER, null, false);
private Tag mTagName;
private Tag mTagHouseNr;
private final LineClipper mClipper;
public static void setRenderTheme(RenderTheme theme) {
public void setRenderTheme(RenderTheme theme) {
renderTheme = theme;
renderLevels = theme.getLevels();
}
@ -118,21 +120,21 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
public TileGenerator() {
mClipper = new LineClipper(0, 0, Tile.SIZE, Tile.SIZE, true);
MapElement m = mDebugWay = new MapElement();
m.startLine();
int s = Tile.SIZE;
m.addPoint(0, 0);
m.addPoint(0, s);
m.addPoint(s, s);
m.addPoint(s, 0);
m.addPoint(0, 0);
m.tags = new Tag[] { new Tag("debug", "box") };
m.geometryType = GEOM_LINE;
MapElement m = mDebugWay = new MapElement();
m.startLine();
int s = Tile.SIZE;
m.addPoint(0, 0);
m.addPoint(0, s);
m.addPoint(s, s);
m.addPoint(s, 0);
m.addPoint(0, 0);
m.tags = new Tag[] { new Tag("debug", "box") };
m.geometryType = GEOM_LINE;
m = mDebugPoint= new MapElement();
m.startPoints();
m.addPoint(s >> 1, 10);
m.geometryType = GEOM_POINT;
m = mDebugPoint = new MapElement();
m.startPoints();
m.addPoint(s >> 1, 10);
m.geometryType = GEOM_POINT;
}
public void cleanup() {
@ -309,7 +311,6 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mElement = null;
}
private void debugUnmatched(boolean closed, Tag[] tags) {
Log.d(TAG, "DBG way not matched: " + closed + " "
+ Arrays.deepToString(tags));

View File

@ -0,0 +1,332 @@
/*
* Copyright 2013 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.layers.tile;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.database.IMapDatabase;
import org.oscim.database.IMapDatabase.OpenResult;
import org.oscim.database.MapDatabaseFactory;
import org.oscim.database.MapDatabases;
import org.oscim.database.MapInfo;
import org.oscim.database.MapOptions;
import org.oscim.layers.Layer;
import org.oscim.renderer.GLRenderer;
import org.oscim.theme.ExternalRenderTheme;
import org.oscim.theme.InternalRenderTheme;
import org.oscim.theme.RenderTheme;
import org.oscim.theme.RenderThemeHandler;
import org.oscim.theme.Theme;
import org.oscim.view.MapView;
import org.xml.sax.SAXException;
import android.util.Log;
public class TileLayer extends Layer {
private final static String TAG = TileLayer.class.getName();
private boolean mClearMap = true;
private final TileManager mTileManager;
private final JobQueue mJobQueue;
// TODO use 1 download and 1 generator thread instead
private final MapWorker mMapWorkers[];
private final int mNumMapWorkers = 4;
private final TileGenerator mTileGenerators[];
public TileLayer(MapView mapView) {
super(mapView);
mTileManager = new TileManager(mapView, this);
mJobQueue = new JobQueue();
mMapWorkers = new MapWorker[mNumMapWorkers];
mTileGenerators = new TileGenerator[mNumMapWorkers];
TileGenerator.setDebugSettings(mapView.getDebugSettings());
for (int i = 0; i < mNumMapWorkers; i++) {
mTileGenerators[i] = new TileGenerator();
mMapWorkers[i] = new MapWorker(i, mJobQueue, mTileGenerators[i], mTileManager);
mMapWorkers[i].start();
}
mLayer = new TileRenderLayer(mapView, mTileManager);
}
public TileRenderLayer getTileLayer(){
return (TileRenderLayer)mLayer;
}
@Override
public void onUpdate(MapPosition mapPosition, boolean changed) {
if (mClearMap){
mTileManager.init(mMapView.getWidth(), mMapView.getHeight());
mClearMap = false;
changed = true;
Log.d(TAG, "init TileManager ----- ");
}
if (changed)
mTileManager.update(mapPosition);
}
@Override
public void destroy(){
mTileManager.destroy();
for (MapWorker mapWorker : mMapWorkers) {
mapWorker.pause();
mapWorker.interrupt();
mapWorker.getTileGenerator().getMapDatabase().close();
try {
mapWorker.join(10000);
} catch (InterruptedException e) {
// restore the interrupted status
Thread.currentThread().interrupt();
}
}
}
private void clearMap() {
// clear tile and overlay data before next draw
mClearMap = true;
}
private MapOptions mMapOptions;
private IMapDatabase mMapDatabase;
private String mRenderTheme;
/**
* Sets the MapDatabase for this MapView.
*
* @param options
* the new MapDatabase options.
* @return true if MapDatabase changed
*/
public boolean setMapDatabase(MapOptions options) {
Log.i(TAG, "setMapDatabase: " + options.db.name());
if (mMapOptions != null && mMapOptions.equals(options))
return true;
mapWorkersPause(true);
mJobQueue.clear();
mMapOptions = options;
mMapDatabase = null;
for (int i = 0; i < mNumMapWorkers; i++) {
MapWorker mapWorker = mMapWorkers[i];
IMapDatabase mapDatabase = MapDatabaseFactory
.createMapDatabase(options.db);
OpenResult result = mapDatabase.open(options);
if (result != OpenResult.SUCCESS) {
Log.d(TAG, "failed open db: " + result.getErrorMessage());
}
TileGenerator tileGenerator = mapWorker.getTileGenerator();
tileGenerator.setMapDatabase(mapDatabase);
// TODO this could be done in a cleaner way..
if (mMapDatabase == null)
mMapDatabase = mapDatabase;
}
if (options.db == MapDatabases.OSCIMAP_READER ||
options.db == MapDatabases.MAP_READER ||
options.db == MapDatabases.TEST_READER)
MapView.enableClosePolygons = true;
else
MapView.enableClosePolygons = false;
clearMap();
mapWorkersProceed();
return true;
}
public Map<String, String> getMapOptions() {
return mMapOptions;
}
public MapPosition getMapFileCenter() {
if (mMapDatabase == null)
return null;
MapInfo mapInfo = mMapDatabase.getMapInfo();
if (mapInfo == null)
return null;
GeoPoint startPos = mapInfo.startPosition;
if (startPos == null)
startPos = mapInfo.mapCenter;
if (startPos == null)
startPos = new GeoPoint(0, 0);
MapPosition mapPosition = new MapPosition();
mapPosition.setPosition(startPos);
if (mapInfo.startZoomLevel == null)
mapPosition.setZoomLevel(12);
else
mapPosition.setZoomLevel((mapInfo.startZoomLevel).byteValue());
return mapPosition;
}
public String getRenderTheme() {
return mRenderTheme;
}
/**
* Sets the internal theme which is used for rendering the map.
*
* @param internalRenderTheme
* the internal rendering theme.
* @return ...
* @throws IllegalArgumentException
* if the supplied internalRenderTheme is null.
*/
public boolean setRenderTheme(InternalRenderTheme internalRenderTheme) {
if (internalRenderTheme == null) {
throw new IllegalArgumentException("render theme must not be null");
}
if (internalRenderTheme.name() == mRenderTheme)
return true;
boolean ret = setRenderTheme((Theme) internalRenderTheme);
if (ret) {
mRenderTheme = internalRenderTheme.name();
}
clearMap();
return ret;
}
/**
* Sets the theme file which is used for rendering the map.
*
* @param renderThemePath
* the path to the XML file which defines the rendering theme.
* @throws IllegalArgumentException
* if the supplied internalRenderTheme is null.
* @throws FileNotFoundException
* if the supplied file does not exist, is a directory or cannot
* be read.
*/
public void setRenderTheme(String renderThemePath) throws FileNotFoundException {
if (renderThemePath == null) {
throw new IllegalArgumentException("render theme path must not be null");
}
boolean ret = setRenderTheme(new ExternalRenderTheme(renderThemePath));
if (ret) {
mRenderTheme = renderThemePath;
}
clearMap();
}
private boolean setRenderTheme(Theme theme) {
mapWorkersPause(true);
InputStream inputStream = null;
try {
inputStream = theme.getRenderThemeAsStream();
RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream);
t.scaleTextSize(1 + (MapView.dpi / 240 - 1) * 0.5f);
// FIXME !!!
GLRenderer.setRenderTheme(t);
for (TileGenerator g : mTileGenerators)
g.setRenderTheme(t);
return true;
} catch (ParserConfigurationException e) {
Log.e(TAG, e.getMessage());
} catch (SAXException e) {
Log.e(TAG, e.getMessage());
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
mapWorkersProceed();
}
return false;
}
/**
* add jobs and remember MapWorkers that stuff needs to be done
*
* @param jobs
* tile jobs
*/
public void addJobs(JobTile[] jobs) {
if (jobs == null) {
mJobQueue.clear();
return;
}
mJobQueue.setJobs(jobs);
for (int i = 0; i < mNumMapWorkers; i++) {
MapWorker m = mMapWorkers[i];
synchronized (m) {
m.notify();
}
}
}
private void mapWorkersPause(boolean wait) {
for (MapWorker mapWorker : mMapWorkers) {
if (!mapWorker.isPausing())
mapWorker.pause();
}
if (wait) {
for (MapWorker mapWorker : mMapWorkers) {
if (!mapWorker.isPausing())
mapWorker.awaitPausing();
}
}
}
private void mapWorkersProceed() {
for (MapWorker mapWorker : mMapWorkers)
mapWorker.proceed();
}
}

View File

@ -13,21 +13,24 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
package org.oscim.layers.tile;
import static org.oscim.generator.JobTile.STATE_LOADING;
import static org.oscim.generator.JobTile.STATE_NEW_DATA;
import static org.oscim.generator.JobTile.STATE_NONE;
import static org.oscim.layers.tile.JobTile.STATE_LOADING;
import static org.oscim.layers.tile.JobTile.STATE_NEW_DATA;
import static org.oscim.layers.tile.JobTile.STATE_NONE;
import java.util.ArrayList;
import java.util.Arrays;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.generator.JobTile;
import org.oscim.generator.TileDistanceSort;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.ScanBox;
import org.oscim.renderer.layer.TextItem;
import org.oscim.utils.FastMath;
import org.oscim.utils.quadtree.QuadTree;
import org.oscim.utils.quadtree.QuadTreeIndex;
import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition;
@ -40,6 +43,8 @@ import android.util.Log;
* - make it general for reuse in tile-overlays
*/
public class TileManager {
private static final int CACHE_TILES_MAX = 250;
static final String TAG = TileManager.class.getSimpleName();
private final static int MAX_ZOOMLEVEL = 17;
private final static int MIN_ZOOMLEVEL = 2;
@ -71,21 +76,53 @@ public class TileManager {
private final ArrayList<JobTile> mJobs;
// counter to check whether current TileSet has changed
private static int mUpdateSerial;
private int mUpdateSerial;
// lock for TileSets while updating MapTile locks
private final Object mTilelock = new Object();
// need to keep track of TileSets to clear on reset...
private final ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(4);
private TileSet mCurrentTiles;
/* package */TileSet mNewTiles;
private final float[] mBoxCoords = new float[8];
public TileManager(MapView mapView) {
private final QuadTreeIndex<MapTile> mIndex = new QuadTreeIndex<MapTile>(){
@Override
public MapTile create(int x, int y, int z) {
QuadTree<MapTile> t = super.add(x, y, z);
t.item = new MapTile(x, y, (byte)z);
t.item.rel = t;
return t.item;
}
@Override
public void remove(MapTile t) {
if (t.rel == null) {
Log.d(TAG, "BUG already removed " + t);
return;
}
super.remove(t.rel);
t.rel.item = null;
t.rel = null;
}
};
private final float[] mBoxCoords = new float[8];
private final TileLayer mTileLayer;
public TileManager(MapView mapView, TileLayer tileLayer) {
mMapView = mapView;
mTileLayer = tileLayer;
mMapViewPosition = mapView.getMapViewPosition();
mJobs = new ArrayList<JobTile>();
mTiles = new MapTile[GLRenderer.CACHE_TILES];
mTiles = new MapTile[CACHE_TILES_MAX];
mTilesSize = 0;
mTilesForUpload = 0;
@ -119,7 +156,7 @@ public class TileManager {
//}
// clear cache index
QuadTree.init();
//QuadTree.init();
// clear references to cached MapTiles
Arrays.fill(mTiles, null);
@ -160,7 +197,7 @@ public class TileManager {
// start with old jobs while new jobs are calculated, which
// should increase the chance that they are free when new
// jobs come in.
mMapView.addJobs(null);
mTileLayer.addJobs(null);
// load some tiles more than currently visible (* 0.75)
double scale = pos.scale * 0.9f;
@ -223,11 +260,11 @@ public class TileManager {
updateTileDistances(jobs, jobs.length, pos);
// sets tiles to state == LOADING
mMapView.addJobs(jobs);
mTileLayer.addJobs(jobs);
mJobs.clear();
/* limit cache items */
int remove = mTilesCount - GLRenderer.CACHE_TILES;
int remove = mTilesCount - CACHE_TILES_MAX;
if (remove > CACHE_THRESHOLD ||
mTilesForUpload > MAX_TILES_IN_QUEUE)
@ -235,9 +272,13 @@ public class TileManager {
limitCache(pos, remove);
}
// need to keep track of TileSets to clear on reset...
private static ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(2);
/**
* Retrive a TileSet of current tiles.
* Tiles remain locked in cache until the set is unlocked by either passing
* it again to this function or to releaseTiles. If passed TileSet is null
* it will be allocated.
*/
public TileSet getActiveTiles(TileSet td) {
if (mCurrentTiles == null)
return td;
@ -287,11 +328,11 @@ public class TileManager {
/* package */MapTile addTile(int x, int y, int zoomLevel) {
MapTile tile;
tile = QuadTree.getTile(x, y, zoomLevel);
//tile = QuadTree.getTile(x, y, zoomLevel);
tile = mIndex.getTile(x, y, zoomLevel);
if (tile == null) {
tile = new MapTile(x, y, (byte) zoomLevel);
QuadTree.add(tile);
tile = mIndex.create(x, y, zoomLevel);
mJobs.add(tile);
addToCache(tile);
} else if (!tile.isActive()) {
@ -302,11 +343,11 @@ public class TileManager {
boolean add = false;
// prefetch parent
MapTile p = tile.rel.parent.tile;
MapTile p = tile.rel.parent.item;
if (p == null) {
p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1));
QuadTree.add(p);
p = mIndex.create(x >> 1, y >> 1, zoomLevel - 1);
addToCache(p);
add = true;
}
@ -319,11 +360,10 @@ public class TileManager {
if (zoomLevel > 3) {
// prefetch grand parent
p = tile.rel.parent.parent.tile;
p = tile.rel.parent.parent.item;
add = false;
if (p == null) {
p = new MapTile(x >> 2, y >> 2, (byte) (zoomLevel - 2));
QuadTree.add(p);
p = mIndex.create(x >> 2, y >> 2, zoomLevel - 2);
addToCache(p);
add = true;
}
@ -376,7 +416,9 @@ public class TileManager {
TextItem.pool.releaseAll(t.labels);
QuadTree.remove(t);
mIndex.remove(t);
//QuadTree.remove(t);
t.state = STATE_NONE;
mTilesCount--;
@ -555,7 +597,7 @@ public class TileManager {
private final ScanBox mScanBox = new ScanBox() {
@Override
public void setVisible(int y, int x1, int x2) {
protected void setVisible(int y, int x1, int x2) {
MapTile[] tiles = mNewTiles.tiles;
int cnt = mNewTiles.cnt;
int maxTiles = tiles.length;

View File

@ -0,0 +1,310 @@
/*
* Copyright 2013 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.layers.tile;
import static org.oscim.layers.tile.JobTile.STATE_NEW_DATA;
import static org.oscim.layers.tile.JobTile.STATE_READY;
import org.oscim.core.MapPosition;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.ScanBox;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.view.MapView;
import android.util.Log;
public class TileRenderLayer extends RenderOverlay {
private final static String TAG = TileRenderLayer.class.getName();
private final float[] mBoxCoords;
private final TileManager mTileManager;
public TileRenderLayer(MapView mapView, TileManager tileManager) {
super(mapView);
mTileManager = tileManager;
mBoxCoords = new float[8];
}
@Override
public void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged,
Matrices matrices) {
int serial = 0;
mMapPosition.copy(curPos);
if (mDrawTiles != null)
serial = mDrawTiles.getSerial();
synchronized (tilelock) {
// get current tiles to draw
mDrawTiles = mTileManager.getActiveTiles(mDrawTiles);
}
if (mDrawTiles == null || mDrawTiles.cnt == 0)
return;
if (positionChanged)
mMapView.getMapViewPosition().getMapViewProjection(mBoxCoords);
boolean changed = false;
//boolean positionChanged = false;
// check if the tiles have changed...
if (serial != mDrawTiles.getSerial()) {
changed = true;
// FIXME needed?
//positionChanged = true;
}
int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
if (changed || positionChanged)
updateTileVisibility();
tileCnt += mNumTileHolder;
/* prepare tile for rendering */
int uploadCnt = compileTileLayers(tiles, tileCnt);
tilesChanged |= (uploadCnt > 0);
TileRenderer.draw(tiles, tileCnt, curPos, matrices);
}
@Override
public void compile() {
}
@Override
public void render(MapPosition pos, Matrices m) {
}
/** compile tile layer data and upload to VBOs */
private static int compileTileLayers(MapTile[] tiles, int tileCnt) {
int uploadCnt = 0;
for (int i = 0; i < tileCnt; i++) {
MapTile tile = tiles[i];
if (!tile.isVisible)
continue;
if (tile.state == STATE_READY)
continue;
if (tile.state == STATE_NEW_DATA) {
uploadTileData(tile);
continue;
}
if (tile.holder != null) {
// load tile that is referenced by this holder
if (tile.holder.state == STATE_NEW_DATA)
uploadTileData(tile.holder);
tile.state = tile.holder.state;
continue;
}
// check near relatives than can serve as proxy
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
MapTile rel = tile.rel.parent.item;
if (rel.state == STATE_NEW_DATA)
uploadTileData(rel);
// dont load child proxies
continue;
}
for (int c = 0; c < 4; c++) {
if ((tile.proxies & 1 << c) == 0)
continue;
MapTile rel = tile.rel.get(i);
if (rel != null && rel.state == STATE_NEW_DATA)
uploadTileData(rel);
}
}
if (uploadCnt > 0)
GLRenderer.checkBufferUsage(false);
return uploadCnt;
}
private static void uploadTileData(MapTile tile) {
tile.state = STATE_READY;
if (tile.layers == null)
return;
int newSize = tile.layers.getSize();
if (newSize > 0) {
if (tile.layers.vbo == null)
tile.layers.vbo = BufferObject.get(newSize);
if (!GLRenderer.uploadLayers(tile.layers, newSize, true)) {
Log.d(TAG, "BUG uploadTileData " + tile + " failed!");
BufferObject.release(tile.layers.vbo);
tile.layers.vbo = null;
tile.layers.clear();
tile.layers = null;
}
}
}
private final Object tilelock = new Object();
/** set tile isVisible flag true for tiles that intersect view */
private void updateTileVisibility() {
// lock tiles while updating isVisible state
synchronized (tilelock) {
MapPosition pos = mMapPosition;
MapTile[] tiles = mDrawTiles.tiles;
int tileZoom = tiles[0].zoomLevel;
for (int i = 0; i < mDrawTiles.cnt; i++)
tiles[i].isVisible = false;
// count placeholder tiles
mNumTileHolder = 0;
// check visibile tiles
mScanBox.scan(pos.x, pos.y, pos.scale, tileZoom, mBoxCoords);
}
}
// get a TileSet of currently visible tiles
public TileSet getVisibleTiles(TileSet td) {
if (mDrawTiles == null)
return td;
// ensure tiles keep visible state
synchronized (tilelock) {
MapTile[] newTiles = mDrawTiles.tiles;
int cnt = mDrawTiles.cnt;
if (td == null)
td = new TileSet(newTiles.length);
// unlock previous tiles
for (int i = 0; i < td.cnt; i++)
td.tiles[i].unlock();
// lock tiles to not be removed from cache
td.cnt = 0;
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible && t.state == STATE_READY) {
t.lock();
td.tiles[td.cnt++] = t;
}
}
}
return td;
}
public void releaseTiles(TileSet td) {
for (int i = 0; i < td.cnt; i++) {
td.tiles[i].unlock();
td.tiles[i] = null;
}
td.cnt = 0;
}
// Add additional tiles that serve as placeholer when flipping
// over date-line.
// I dont really like this but cannot think of a better solution:
// the other option would be to run scanbox each time for upload,
// drawing, proxies and text layer. needing to add placeholder only
// happens rarely, unless you live on Fidschi
/* package */int mNumTileHolder;
/* package */TileSet mDrawTiles;
// scanline fill class used to check tile visibility
private final ScanBox mScanBox = new ScanBox() {
@Override
protected void setVisible(int y, int x1, int x2) {
int cnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
for (int i = 0; i < cnt; i++) {
MapTile t = tiles[i];
if (t.tileY == y && t.tileX >= x1 && t.tileX < x2)
t.isVisible = true;
}
int xmax = 1 << mZoom;
if (x1 >= 0 && x2 < xmax)
return;
// add placeholder tiles to show both sides
// of date line. a little too complicated...
for (int x = x1; x < x2; x++) {
MapTile holder = null;
MapTile tile = null;
boolean found = false;
if (x >= 0 && x < xmax)
continue;
int xx = x;
if (x < 0)
xx = xmax + x;
else
xx = x - xmax;
if (xx < 0 || xx >= xmax)
continue;
for (int i = cnt; i < cnt + mNumTileHolder; i++)
if (tiles[i].tileX == x && tiles[i].tileY == y) {
found = true;
break;
}
if (found)
continue;
for (int i = 0; i < cnt; i++)
if (tiles[i].tileX == xx && tiles[i].tileY == y) {
tile = tiles[i];
break;
}
if (tile == null)
continue;
holder = new MapTile(x, y, (byte) mZoom);
holder.isVisible = true;
holder.holder = tile;
tile.isVisible = true;
tiles[cnt + mNumTileHolder++] = holder;
}
}
};
}

View File

@ -12,18 +12,23 @@
* 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;
package org.oscim.layers.tile;
import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.glStencilMask;
import static org.oscim.generator.JobTile.STATE_READY;
import static org.oscim.layers.tile.JobTile.STATE_READY;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.LineRenderer;
import org.oscim.renderer.LineTexRenderer;
import org.oscim.renderer.PolygonRenderer;
import org.oscim.renderer.layer.Layer;
import org.oscim.utils.FastMath;
import org.oscim.utils.Matrix4;
import org.oscim.utils.quadtree.QuadTree;
import android.opengl.GLES20;
@ -56,6 +61,8 @@ public class TileRenderer {
// discard z projection from tilt
mProjMatrix.setValue(10, 0);
mProjMatrix.setValue(14, 0);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
//GLES20.GL_STENCIL_BUFFER_BIT);
GLES20.glDepthFunc(GLES20.GL_LESS);
@ -188,7 +195,7 @@ public class TileRenderer {
if ((tile.proxies & 1 << i) == 0)
continue;
MapTile c = tile.rel.child[i].tile;
MapTile c = tile.rel.get(i);
if (c.state == STATE_READY) {
drawTile(c, pos);
@ -200,10 +207,9 @@ public class TileRenderer {
// just FIXME!
private static void drawProxyTile(MapTile tile, MapPosition pos, boolean parent, boolean preferParent) {
//int diff = pos.zoomLevel - tile.zoomLevel;
QuadTree r = tile.rel;
MapTile proxy;
QuadTree<MapTile> r = tile.rel;
MapTile proxy;
if (!preferParent) {
// prefer drawing children
@ -213,7 +219,7 @@ public class TileRenderer {
if (parent) {
// draw parent proxy
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
proxy = r.parent.tile;
proxy = r.parent.item;
if (proxy.state == STATE_READY) {
//Log.d(TAG, "1. draw parent " + proxy);
drawTile(proxy, pos);
@ -222,12 +228,12 @@ public class TileRenderer {
} else if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
// check if parent was already drawn
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
proxy = r.parent.tile;
proxy = r.parent.item;
if (proxy.state == STATE_READY)
return;
}
proxy = r.parent.parent.tile;
proxy = r.parent.parent.item;
if (proxy.state == STATE_READY)
drawTile(proxy, pos);
}
@ -235,7 +241,7 @@ public class TileRenderer {
// prefer drawing parent
if (parent) {
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
proxy = r.parent.tile;
proxy = r.parent.item;
if (proxy != null && proxy.state == STATE_READY) {
//Log.d(TAG, "2. draw parent " + proxy);
drawTile(proxy, pos);
@ -248,7 +254,7 @@ public class TileRenderer {
} else if ((tile.proxies & MapTile.PROXY_GRAMPA) != 0) {
// check if parent was already drawn
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
proxy = r.parent.tile;
proxy = r.parent.item;
if (proxy.state == STATE_READY)
return;
}
@ -256,7 +262,7 @@ public class TileRenderer {
if (drawProxyChild(tile, pos) > 0)
return;
proxy = r.parent.parent.tile;
proxy = r.parent.parent.item;
if (proxy.state == STATE_READY)
drawTile(proxy, pos);
}

View File

@ -12,10 +12,11 @@
* 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;
package org.oscim.layers.tile;
import java.util.Comparator;
/**
* use with TileManager.getActiveTiles(TileSet) to get the current tiles. tiles
* are locked to not be modifed until getActiveTiles passes them back on a
@ -27,10 +28,14 @@ public final class TileSet {
int serial;
public int getSerial(){
return serial;
}
TileSet() {
}
TileSet(int numTiles) {
public TileSet(int numTiles) {
tiles = new MapTile[numTiles];
}

View File

@ -13,10 +13,9 @@
* 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.generator;
package org.oscim.layers.tile;
import org.oscim.core.Tile;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.GeometryUtils;

View File

@ -30,9 +30,9 @@ public class BuildingOverlay extends Overlay {
final ExtrusionOverlay mExtLayer;
public BuildingOverlay(MapView mapView) {
public BuildingOverlay(MapView mapView, org.oscim.layers.tile.TileRenderLayer tileRenderLayer) {
super(mapView);
mExtLayer = new ExtrusionOverlay(mapView);
mExtLayer = new ExtrusionOverlay(mapView, tileRenderLayer);
mLayer = mExtLayer;
}

View File

@ -14,6 +14,7 @@
*/
package org.oscim.overlay;
import org.oscim.layers.tile.TileRenderLayer;
import org.oscim.renderer.overlays.TextOverlay;
import org.oscim.view.MapView;
@ -27,9 +28,9 @@ public class LabelingOverlay extends Overlay {
private final static String TAG = LabelingOverlay.class.getName();
final TextOverlay mTextLayer;
public LabelingOverlay(MapView mapView) {
public LabelingOverlay(MapView mapView, TileRenderLayer tileRenderLayer) {
super(mapView);
mTextLayer = new TextOverlay(mapView);
mTextLayer = new TextOverlay(mapView, tileRenderLayer);
mLayer = mTextLayer;
}

View File

@ -16,14 +16,10 @@
package org.oscim.overlay;
import org.oscim.core.MapPosition;
import org.oscim.core.PointF;
import org.oscim.renderer.overlays.RenderOverlay;
import org.oscim.layers.InputLayer;
import org.oscim.view.MapView;
import android.view.KeyEvent;
import android.view.MotionEvent;
/**
* Base class representing an overlay which may be displayed on top of a
* {@link MapView}. To add an overlay, subclass this class, create an instance,
@ -34,85 +30,37 @@ import android.view.MotionEvent;
*
* @author Nicolas Gramlich
*/
public abstract class Overlay {
// ===========================================================
// Constants
// ===========================================================
//private static AtomicInteger sOrdinal = new AtomicInteger();
// From Google Maps API
//protected static final float SHADOW_X_SKEW = -0.8999999761581421f;
//protected static final float SHADOW_Y_SCALE = 0.5f;
// ===========================================================
// Fields
// ===========================================================
private boolean mEnabled = true;
private boolean mReceiveEvents = true;
protected final MapView mMapView;
/** RenderOverlay used to draw this layer. To be implemented by sub-classes */
protected RenderOverlay mLayer;
public RenderOverlay getLayer() {
return mLayer;
}
// ===========================================================
// Constructors
// ===========================================================
public abstract class Overlay extends InputLayer {
public Overlay(MapView mapView) {
mMapView = mapView;
}
// ===========================================================
// Getter & Setter
// ===========================================================
/**
* Sets whether the Overlay is marked to be enabled. This setting does
* nothing by default, but should be checked before calling draw().
*
* @param pEnabled
* ...
*/
public void setEnabled(boolean pEnabled) {
mEnabled = pEnabled;
super(mapView);
}
/**
* Specifies if the Overlay is marked to be enabled. This should be checked
* before calling draw().
* TBD
*
* Interface definition for overlays that contain items that can be snapped
* to (for example, when the user invokes a zoom, this could be called
* allowing the user to snap the zoom to an interesting point.)
*
* @return true if the Overlay is marked enabled, false otherwise
*/
public boolean isEnabled() {
return mEnabled;
}
public interface Snappable {
/**
* Sets whether the Overlay is marked to be receive touch exents.
*
* @param pEnabled
* ...
*/
public void setEnableEvents(boolean pEnabled) {
mReceiveEvents = pEnabled;
}
/**
* Specifies if the Overlay is marked to be enabled. This should be checked
* before calling draw().
*
* @return true if the Overlay is marked enabled, false otherwise
*/
public boolean eventsEnabled() {
return mReceiveEvents;
/**
* Checks to see if the given x and y are close enough to an item
* resulting in snapping the current action (e.g. zoom) to the item.
*
* @param x
* The x in screen coordinates.
* @param y
* The y in screen coordinates.
* @param snapPoint
* To be filled with the the interesting point (in screen
* coordinates) that is closest to the given x and y. Can be
* untouched if not snapping.
* @return Whether or not to snap to the interesting point.
*/
boolean onSnapToItem(int x, int y, PointF snapPoint);
}
///**
@ -139,256 +87,4 @@ public abstract class Overlay {
//protected final static int getSafeMenuIdSequence(int count) {
// return sOrdinal.getAndAdd(count);
//}
// ===========================================================
// Methods
// ===========================================================
/**
* Called before each frame render request.
*
* @param mapPosition
* current MapPosition
* @param changed
* true when MapPosition has changed since last call
*/
public void onUpdate(MapPosition mapPosition, boolean changed) {
}
/**
* Override to perform clean up of resources before shutdown. By default
* does nothing.
*/
public void onDetach() {
// FIXME call to this function is not implemented
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param keyCode
* ...
* @param event
* ...
* @return ...
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param keyCode
* ...
* @param event
* ...
* @return ...
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
/**
* <b>You can prevent all(!) other Touch-related events from happening!</b><br />
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onTouchEvent(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onTrackballEvent(MotionEvent e) {
return false;
}
/** GestureDetector.OnDoubleTapListener **/
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onDoubleTap(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
/** OnGestureListener **/
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onDown(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param pEvent1
* ...
* @param pEvent2
* ...
* @param pVelocityX
* ...
* @param pVelocityY
* ...
* @return ...
*/
public boolean onFling(MotionEvent pEvent1, MotionEvent pEvent2,
float pVelocityX, float pVelocityY) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onLongPress(MotionEvent e) {
return false;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param pEvent1
* ...
* @param pEvent2
* ...
* @param pDistanceX
* ...
* @param pDistanceY
* ...
* @return ...
*/
public boolean onScroll(MotionEvent pEvent1, MotionEvent pEvent2,
float pDistanceX, float pDistanceY) {
return false;
}
/**
* @param pEvent
* ...
*/
public void onShowPress(MotionEvent pEvent) {
return;
}
/**
* By default does nothing (<code>return false</code>). If you handled the
* Event, return <code>true</code>, otherwise return <code>false</code>. If
* you returned <code>true</code> none of the following Overlays or the
* underlying {@link MapView} has the chance to handle this event.
*
* @param e
* ...
* @return ...
*/
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
/**
* Interface definition for overlays that contain items that can be snapped
* to (for example, when the user invokes a zoom, this could be called
* allowing the user to snap the zoom to an interesting point.)
*/
public interface Snappable {
/**
* Checks to see if the given x and y are close enough to an item
* resulting in snapping the current action (e.g. zoom) to the item.
*
* @param x
* The x in screen coordinates.
* @param y
* The y in screen coordinates.
* @param snapPoint
* To be filled with the the interesting point (in screen
* coordinates) that is closest to the given x and y. Can be
* untouched if not snapping.
* @return Whether or not to snap to the interesting point.
*/
boolean onSnapToItem(int x, int y, PointF snapPoint);
}
}

View File

@ -25,6 +25,7 @@ import org.oscim.core.MercatorProjection;
import org.oscim.core.PointD;
import org.oscim.core.Tile;
import org.oscim.graphics.Paint.Cap;
import org.oscim.layers.Layer;
import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.overlays.BasicOverlay;
@ -34,7 +35,7 @@ import org.oscim.utils.LineClipper;
import org.oscim.view.MapView;
/** This class draws a path line in given color. */
public class PathOverlay extends Overlay {
public class PathOverlay extends Layer {
/** Stores points, converted to the map projection. */
/* package */protected final ArrayList<GeoPoint> mPoints;

View File

@ -18,8 +18,6 @@ import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_DYNAMIC_DRAW;
import static android.opengl.GLES20.GL_ONE;
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
import static org.oscim.generator.JobTile.STATE_NEW_DATA;
import static org.oscim.generator.JobTile.STATE_READY;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -31,6 +29,7 @@ import javax.microedition.khronos.opengles.GL10;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.TextureItem;
import org.oscim.renderer.overlays.RenderOverlay;
@ -62,7 +61,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
static int CACHE_TILES = CACHE_TILES_MAX;
private static MapView mMapView;
static int screenWidth, screenHeight;
public static int screenWidth, screenHeight;
private static MapViewPosition mMapViewPosition;
private static MapPosition mMapPosition;
@ -76,6 +75,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static float[] mBoxCoords;
public class Matrices {
// do not modify any of these
public final Matrix4 viewproj = new Matrix4();
public final Matrix4 proj = new Matrix4();
public final Matrix4 view = new Matrix4();
@ -89,88 +89,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
//private
static float[] mClearColor = null;
static int mQuadIndicesID;
final static int maxQuads = 64;
public static int mQuadIndicesID;
public final static int maxQuads = 64;
private static boolean mUpdateColor = false;
// drawlock to synchronize Main- and GL-Thread
// static ReentrantLock tilelock = new ReentrantLock();
static ReentrantLock drawlock = new ReentrantLock();
// Add additional tiles that serve as placeholer when flipping
// over date-line.
// I dont really like this but cannot think of a better solution:
// the other option would be to run scanbox each time for upload,
// drawing, proxies and text layer. needing to add placeholder only
// happens rarely, unless you live on Fidschi
/* package */static int mNumTileHolder;
/* package */static TileSet mDrawTiles;
// scanline fill class used to check tile visibility
private static ScanBox mScanBox = new ScanBox() {
@Override
void setVisible(int y, int x1, int x2) {
int cnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
for (int i = 0; i < cnt; i++) {
MapTile t = tiles[i];
if (t.tileY == y && t.tileX >= x1 && t.tileX < x2)
t.isVisible = true;
}
int xmax = 1 << mZoom;
if (x1 >= 0 && x2 < xmax)
return;
// add placeholder tiles to show both sides
// of date line. a little too complicated...
for (int x = x1; x < x2; x++) {
MapTile holder = null;
MapTile tile = null;
boolean found = false;
if (x >= 0 && x < xmax)
continue;
int xx = x;
if (x < 0)
xx = xmax + x;
else
xx = x - xmax;
if (xx < 0 || xx >= xmax)
continue;
for (int i = cnt; i < cnt + mNumTileHolder; i++)
if (tiles[i].tileX == x && tiles[i].tileY == y) {
found = true;
break;
}
if (found)
continue;
for (int i = 0; i < cnt; i++)
if (tiles[i].tileX == xx && tiles[i].tileY == y) {
tile = tiles[i];
break;
}
if (tile == null)
continue;
holder = new MapTile(x, y, (byte) mZoom);
holder.isVisible = true;
holder.holder = tile;
tile.isVisible = true;
tiles[cnt + mNumTileHolder++] = holder;
}
}
};
public static ReentrantLock drawlock = new ReentrantLock();
/**
* @param mapView
@ -204,8 +130,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mUpdateColor = true;
}
private static int uploadCnt = 0;
public static boolean uploadLayers(Layers layers, int newSize,
boolean addFill) {
@ -258,7 +182,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
return true;
}
private static void checkBufferUsage(boolean force) {
public static void checkBufferUsage(boolean force) {
// try to clear some unused vbo when exceding limit
if (!force && mBufferMemoryUsage < LIMIT_BUFFERS) {
@ -300,100 +224,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
}
}
private static Object tilelock = new Object();
/** set tile isVisible flag true for tiles that intersect view */
private static void updateTileVisibility() {
MapPosition pos = mMapPosition;
MapTile[] tiles = mDrawTiles.tiles;
// lock tiles while updating isVisible state
synchronized (GLRenderer.tilelock) {
int tileZoom = tiles[0].zoomLevel;
for (int i = 0; i < mDrawTiles.cnt; i++)
tiles[i].isVisible = false;
// count placeholder tiles
mNumTileHolder = 0;
// check visibile tiles
mScanBox.scan(pos.x, pos.y, pos.scale, tileZoom, mBoxCoords);
}
}
private static void uploadTileData(MapTile tile) {
tile.state = STATE_READY;
if (tile.layers == null)
return;
int newSize = tile.layers.getSize();
if (newSize > 0) {
if (tile.layers.vbo == null)
tile.layers.vbo = BufferObject.get(newSize);
if (!uploadLayers(tile.layers, newSize, true)) {
Log.d(TAG, "BUG uploadTileData " + tile + " failed!");
BufferObject.release(tile.layers.vbo);
tile.layers.vbo = null;
tile.layers.clear();
tile.layers = null;
}
}
}
/** compile tile layer data and upload to VBOs */
private static void compileTileLayers(MapTile[] tiles, int tileCnt) {
uploadCnt = 0;
for (int i = 0; i < tileCnt; i++) {
MapTile tile = tiles[i];
if (!tile.isVisible)
continue;
if (tile.state == STATE_READY)
continue;
if (tile.state == STATE_NEW_DATA) {
uploadTileData(tile);
continue;
}
if (tile.holder != null) {
// load tile that is referenced by this holder
if (tile.holder.state == STATE_NEW_DATA)
uploadTileData(tile.holder);
tile.state = tile.holder.state;
continue;
}
// check near relatives than can serve as proxy
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
MapTile rel = tile.rel.parent.tile;
if (rel.state == STATE_NEW_DATA)
uploadTileData(rel);
// dont load child proxies
continue;
}
for (int c = 0; c < 4; c++) {
if ((tile.proxies & 1 << c) == 0)
continue;
MapTile rel = tile.rel.child[c].tile;
if (rel != null && rel.state == STATE_NEW_DATA)
uploadTileData(rel);
}
}
if (uploadCnt > 0)
checkBufferUsage(false);
}
private static void draw() {
long start = 0;
@ -407,35 +238,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mUpdateColor = false;
}
// Note: it seems faster to also clear the stencil buffer even
// when not needed. probaly otherwise it is masked out from the
// depth buffer as they share the same memory region afaik
// or for a better reason see OpenGL Insights chapter 23.
GLES20.glDepthMask(true);
GLES20.glStencilMask(0xFF);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT
| GLES20.GL_DEPTH_BUFFER_BIT
| GLES20.GL_STENCIL_BUFFER_BIT);
int serial = 0;
if (mDrawTiles != null)
serial = mDrawTiles.serial;
// get current tiles to draw
mDrawTiles = mMapView.getTileManager().getActiveTiles(mDrawTiles);
if (mDrawTiles == null || mDrawTiles.cnt == 0)
return;
boolean tilesChanged = false;
boolean positionChanged = false;
// check if the tiles have changed...
if (serial != mDrawTiles.serial) {
tilesChanged = true;
// FIXME needed?
positionChanged = true;
}
boolean tilesChanged = true;
boolean positionChanged = true;
// get current MapPosition, set mBoxCoords (mapping of screen to model
// coordinates)
@ -457,38 +267,22 @@ public class GLRenderer implements GLSurfaceView.Renderer {
}
}
int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
if (positionChanged)
updateTileVisibility();
tileCnt += mNumTileHolder;
/* prepare tile for rendering */
compileTileLayers(tiles, tileCnt);
tilesChanged |= (uploadCnt > 0);
/* update overlays */
RenderOverlay[] overlays = mMapView.getOverlayManager().getRenderLayers();
for (int i = 0, n = overlays.length; i < n; i++)
overlays[i].update(mMapPosition, positionChanged, tilesChanged, mMatrices);
/* draw base layer */
TileRenderer.draw(tiles, tileCnt, pos, mMatrices);
/* draw overlays */
for (int i = 0, n = overlays.length; i < n; i++) {
RenderOverlay renderOverlay = overlays[i];
RenderOverlay renderLayer = overlays[i];
if (renderOverlay.newData) {
renderOverlay.compile();
renderOverlay.newData = false;
if (renderLayer.newData) {
renderLayer.compile();
renderLayer.newData = false;
}
if (renderOverlay.isReady)
renderOverlay.render(mMapPosition, mMatrices);
if (renderLayer.isReady)
renderLayer.render(mMapPosition, mMatrices);
}
if (MapView.debugFrameTime) {
@ -506,44 +300,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
return ((t.tileX % 4) + (t.tileY % 4 * 4) + 1);
}
// get a TileSet of currently visible tiles
public static TileSet getVisibleTiles(TileSet td) {
if (mDrawTiles == null)
return td;
// ensure tiles keep visible state
synchronized (GLRenderer.tilelock) {
MapTile[] newTiles = mDrawTiles.tiles;
int cnt = mDrawTiles.cnt;
if (td == null)
td = new TileSet(newTiles.length);
// unlock previous tiles
for (int i = 0; i < td.cnt; i++)
td.tiles[i].unlock();
// lock tiles to not be removed from cache
td.cnt = 0;
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible && t.state == STATE_READY) {
t.lock();
td.tiles[td.cnt++] = t;
}
}
}
return td;
}
public static void releaseTiles(TileSet td) {
for (int i = 0; i < td.cnt; i++) {
td.tiles[i].unlock();
td.tiles[i] = null;
}
td.cnt = 0;
}
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
Log.d(TAG, "SurfaceChanged:" + mNewSurface + " " + width + "x" + height);
@ -608,7 +364,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
mBufferMemoryUsage = 0;
mDrawTiles = null;
//mDrawTiles = null;
int numTiles = (screenWidth / (Tile.SIZE / 2) + 2)
* (screenHeight / (Tile.SIZE / 2) + 2);
@ -624,18 +380,21 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mMapView.redrawMap(true);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
// Log.d(TAG, "Extensions: " + ext);
// classes that require GL context for initialization
public static void initRenderer() {
LineRenderer.init();
LineTexRenderer.init();
PolygonRenderer.init();
TextureRenderer.init();
TextureItem.init(10);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Log.d(TAG, GLES20.glGetString(GLES20.GL_EXTENSIONS));
// classes that require GL context for initialization
initRenderer();
mNewSurface = true;
}

View File

@ -1,148 +0,0 @@
/*
* 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;
import android.util.Log;
public class QuadTree {
private static String TAG = QuadTree.class.getName();
// pointer to tile 0/0/0
private static QuadTree root;
// parent pointer is used to link pool items
private static QuadTree pool;
public QuadTree parent;
// .... x y
// 0 => 0 0
// 1 => 1 0
// 2 => 0 1
// 3 => 1 1
public final QuadTree[] child = new QuadTree[4];
int refs = 0;
byte id;
public MapTile tile;
static void init() {
pool = null;
root = new QuadTree();
root.parent = root;
}
static boolean remove(MapTile t) {
if (t.rel == null) {
// Bad Things(tm) happened
Log.d(TAG, "BUG already removed " + t);
return true;
}
QuadTree cur = t.rel;
QuadTree next;
for (; cur != root;) {
// keep pointer to parent
next = cur.parent;
cur.refs--;
// if current node has no children
if (cur.refs == 0) {
// unhook from parent
next.child[cur.id] = null;
// add item back to pool
cur.parent = pool;
pool = cur;
}
cur = next;
}
root.refs--;
t.rel.tile = null;
t.rel = null;
return true;
}
static QuadTree add(MapTile tile) {
int x = tile.tileX;
int y = tile.tileY;
int z = tile.zoomLevel;
// if (x < 0 || x >= 1 << z) {
// Log.d(TAG, "invalid position");
// return null;
// }
// if (y < 0 || y >= 1 << z) {
// Log.d(TAG, "invalid position");
// return null;
// }
QuadTree leaf = root;
for (int level = z - 1; level >= 0; level--) {
int id = ((x >> level) & 1) | ((y >> level) & 1) << 1;
leaf.refs++;
QuadTree cur = leaf.child[id];
if (cur != null) {
leaf = cur;
continue;
}
if (pool != null) {
cur = pool;
pool = pool.parent;
} else {
cur = new QuadTree();
}
cur.refs = 0;
cur.id = (byte) id;
cur.parent = leaf;
cur.parent.child[id] = cur;
leaf = cur;
}
leaf.refs++;
leaf.tile = tile;
tile.rel = leaf;
return leaf;
}
static MapTile getTile(int x, int y, int z) {
QuadTree leaf = root;
for (int level = z - 1; level >= 0; level--) {
leaf = leaf.child[((x >> level) & 1) | ((y >> level) & 1) << 1];
if (leaf == null)
return null;
if (level == 0) {
return leaf.tile;
}
}
return null;
}
}

View File

@ -78,7 +78,7 @@ public abstract class ScanBox {
protected int mZoom;
abstract void setVisible(int y, int x1, int x2);
protected abstract void setVisible(int y, int x1, int x2);
public void scan(double x, double y, double scale, int zoom, float[] box) {
mZoom = zoom;

View File

@ -20,12 +20,13 @@ import java.nio.ShortBuffer;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.generator.JobTile;
import org.oscim.layers.tile.JobTile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileRenderLayer;
import org.oscim.layers.tile.TileSet;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.GLState;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.TileSet;
import org.oscim.renderer.layer.ExtrusionLayer;
import org.oscim.utils.GlUtils;
import org.oscim.view.MapView;
@ -39,8 +40,11 @@ import android.util.Log;
public class ExtrusionOverlay extends RenderOverlay {
private final static String TAG = ExtrusionOverlay.class.getName();
public ExtrusionOverlay(MapView mapView) {
private final TileRenderLayer mTileLayer;
public ExtrusionOverlay(MapView mapView, org.oscim.layers.tile.TileRenderLayer tileRenderLayer) {
super(mapView);
mTileLayer = tileRenderLayer;
}
private static int[] shaderProgram = new int[2];
@ -92,7 +96,10 @@ public class ExtrusionOverlay extends RenderOverlay {
}
int ready = 0;
mTileSet = mMapView.getTileManager().getActiveTiles(mTileSet);
mTileSet = mTileLayer.getVisibleTiles(mTileSet);
if (mTileSet == null)
return;
MapTile[] tiles = mTileSet.tiles;
// FIXME just release tiles in this case
if (mAlpha == 0 || curPos.zoomLevel < 16) {
@ -135,11 +142,12 @@ public class ExtrusionOverlay extends RenderOverlay {
for (int i = 0; i < mTileSet.cnt; i++) {
if (!tiles[i].isVisible)
continue;
MapTile t = tiles[i];
for (byte j = 0; j < 4; j++) {
if ((t.proxies & (1 << j)) != 0) {
MapTile c = t.rel.child[j].tile;
MapTile c = t.rel.get(j);
el = getLayer(c);
if (el == null || !el.compiled)

View File

@ -32,18 +32,19 @@ import java.util.HashMap;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.generator.JobTile;
import org.oscim.graphics.Color;
import org.oscim.graphics.Paint.Cap;
import org.oscim.layers.tile.JobTile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileRenderLayer;
import org.oscim.layers.tile.TileSet;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLRenderer.Matrices;
import org.oscim.renderer.GLState;
import org.oscim.renderer.LineRenderer;
import org.oscim.renderer.MapTile;
import org.oscim.renderer.PolygonRenderer;
import org.oscim.renderer.TextureRenderer;
import org.oscim.renderer.TileSet;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer;
@ -199,11 +200,13 @@ public class TextOverlay extends BasicOverlay {
private float mSquareRadius;
private int mRelabelCnt;
private final TileRenderLayer mTileLayer;
public TextOverlay(MapView mapView) {
public TextOverlay(MapView mapView, TileRenderLayer baseLayer) {
super(mapView);
mMapViewPosition = mapView.getMapViewPosition();
mMapViewPosition = mapView.getMapViewPosition();
mTileLayer = baseLayer;
layers.textureLayers = new TextLayer();
mTmpLayer = new TextLayer();
mActiveTiles = new HashMap<MapTile, LabelTile>();
@ -350,7 +353,9 @@ public class TextOverlay extends BasicOverlay {
return false;
// get current tiles
mTileSet = GLRenderer.getVisibleTiles(mTileSet);
mTileSet = mTileLayer.getVisibleTiles(mTileSet);
if (mTileSet == null)
return false;
if (mTileSet.cnt == 0)
return false;
@ -605,7 +610,7 @@ public class TextOverlay extends BasicOverlay {
tl.labels = null;
// remove tile locks
GLRenderer.releaseTiles(mTileSet);
mTileLayer.releaseTiles(mTileSet);
// pass new labels for rendering
synchronized (this) {

View File

@ -117,6 +117,8 @@ public class RenderThemeHandler extends DefaultHandler {
private int mLevel;
private RenderTheme mRenderTheme;
private final Stack<Rule> mRuleStack = new Stack<Rule>();
private final HashMap<String, RenderInstruction> tmpStyleHash =
new HashMap<String, RenderInstruction>(10);
@Override
public void endDocument() {
@ -148,8 +150,6 @@ public class RenderThemeHandler extends DefaultHandler {
Log.d(TAG, exception.getMessage());
}
private static HashMap<String, RenderInstruction> tmpStyleHash =
new HashMap<String, RenderInstruction>(10);
@Override
public void startElement(String uri, String localName, String qName,

View File

@ -21,7 +21,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.oscim.core.MapPosition;
import org.oscim.core.PointF;
import org.oscim.overlay.Overlay;
import org.oscim.layers.InputLayer;
import org.oscim.layers.Layer;
import org.oscim.overlay.Overlay.Snappable;
import org.oscim.renderer.overlays.RenderOverlay;
@ -32,45 +33,45 @@ import android.view.GestureDetector.OnGestureListener;
import android.view.KeyEvent;
import android.view.MotionEvent;
public class LayerManager extends AbstractList<Overlay> implements OnGestureListener,
public class LayerManager extends AbstractList<Layer> implements OnGestureListener,
OnDoubleTapListener {
private final GestureDetector mGestureDetector;
private final CopyOnWriteArrayList<Overlay> mOverlayList;
private final CopyOnWriteArrayList<Layer> mLayerList;
LayerManager(Context context) {
mOverlayList = new CopyOnWriteArrayList<Overlay>();
mLayerList = new CopyOnWriteArrayList<Layer>();
mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setOnDoubleTapListener(this);
}
@Override
public synchronized Overlay get(final int pIndex) {
return mOverlayList.get(pIndex);
public synchronized Layer get(final int pIndex) {
return mLayerList.get(pIndex);
}
@Override
public synchronized int size() {
return mOverlayList.size();
return mLayerList.size();
}
@Override
public synchronized void add(final int pIndex, final Overlay pElement) {
mOverlayList.add(pIndex, pElement);
mDirtyOverlays = true;
public synchronized void add(final int pIndex, final Layer pElement) {
mLayerList.add(pIndex, pElement);
mDirtyLayers = true;
}
@Override
public synchronized Overlay remove(final int pIndex) {
mDirtyOverlays = true;
return mOverlayList.remove(pIndex);
public synchronized Layer remove(final int pIndex) {
mDirtyLayers = true;
return mLayerList.remove(pIndex);
}
@Override
public synchronized Overlay set(final int pIndex, final Overlay pElement) {
mDirtyOverlays = true;
return mOverlayList.set(pIndex, pElement);
public synchronized Layer set(final int pIndex, final Layer pElement) {
mDirtyLayers = true;
return mLayerList.set(pIndex, pElement);
}
public boolean handleMotionEvent(MotionEvent e) {
@ -84,59 +85,68 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
return false;
}
private boolean mDirtyOverlays;
private boolean mDirtyLayers;
private RenderOverlay[] mDrawLayers;
public RenderOverlay[] getRenderLayers() {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
return mDrawLayers;
}
public void onDetach() {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (Layer o : mLayers)
o.onDetach();
}
Overlay[] mOverlays;
Layer[] mLayers;
InputLayer[] mInputLayer;
private synchronized void updateOverlays() {
if (!mDirtyOverlays)
private synchronized void updateLayers() {
if (!mDirtyLayers)
return;
mOverlays = new Overlay[mOverlayList.size()];
mLayers = new Layer[mLayerList.size()];
int numRenderLayers = 0;
int numInputLayers = 0;
for (int i = 0, n = mOverlayList.size(); i < n; i++) {
Overlay o = mOverlayList.get(i);
for (int i = 0, n = mLayerList.size(); i < n; i++) {
Layer o = mLayerList.get(i);
if (o.getLayer() != null)
numRenderLayers++;
mOverlays[n - i - 1] = o;
if (o instanceof InputLayer)
numInputLayers++;
mLayers[n - i - 1] = o;
}
mDrawLayers = new RenderOverlay[numRenderLayers];
mInputLayer = new InputLayer[numInputLayers];
for (int i = 0, cnt = 0, n = mOverlayList.size(); i < n; i++) {
Overlay o = mOverlayList.get(i);
for (int i = 0, cntR = 0, cntI = 0, n = mLayerList.size(); i < n; i++) {
Layer o = mLayerList.get(i);
RenderOverlay l = o.getLayer();
if (l != null)
mDrawLayers[cnt++] = l;
mDrawLayers[cntR++] = l;
if (o instanceof InputLayer)
mInputLayer[cntI++] = (InputLayer)o;
}
mDirtyOverlays = false;
mDirtyLayers = false;
}
public boolean onTouchEvent(final MotionEvent event) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onTouchEvent(event))
return true;
@ -144,10 +154,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
}
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onKeyDown(keyCode, event))
return true;
@ -155,10 +165,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
}
public boolean onKeyUp(final int keyCode, final KeyEvent event) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onKeyUp(keyCode, event))
return true;
@ -166,10 +176,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
}
public boolean onTrackballEvent(final MotionEvent event) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onTrackballEvent(event))
return true;
@ -177,10 +187,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
}
public boolean onSnapToItem(final int x, final int y, final PointF snapPoint) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o instanceof Snappable)
if (((Snappable) o).onSnapToItem(x, y, snapPoint))
return true;
@ -192,10 +202,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public boolean onDoubleTap(final MotionEvent e) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onDoubleTap(e))
return true;
@ -204,10 +214,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public boolean onDoubleTapEvent(final MotionEvent e) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onDoubleTapEvent(e))
return true;
@ -216,10 +226,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onSingleTapConfirmed(e))
return true;
@ -230,10 +240,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public boolean onDown(final MotionEvent pEvent) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onDown(pEvent))
return true;
@ -243,10 +253,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public boolean onFling(final MotionEvent pEvent1, final MotionEvent pEvent2,
final float pVelocityX, final float pVelocityY) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onFling(pEvent1, pEvent2, pVelocityX, pVelocityY))
return true;
@ -255,10 +265,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public void onLongPress(final MotionEvent pEvent) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onLongPress(pEvent))
return;
}
@ -266,10 +276,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public boolean onScroll(final MotionEvent pEvent1, final MotionEvent pEvent2,
final float pDistanceX, final float pDistanceY) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onScroll(pEvent1, pEvent2, pDistanceX, pDistanceY))
return true;
@ -278,20 +288,20 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
@Override
public void onShowPress(final MotionEvent pEvent) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
o.onShowPress(pEvent);
}
@Override
public boolean onSingleTapUp(final MotionEvent pEvent) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
for (InputLayer o : mInputLayer)
if (o.onSingleTapUp(pEvent))
return true;
@ -299,52 +309,62 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
}
public void onUpdate(MapPosition mapPosition, boolean changed) {
if (mDirtyOverlays)
updateOverlays();
if (mDirtyLayers)
updateLayers();
for (Overlay o : mOverlays)
o.onUpdate(mapPosition, changed);
for (Layer l : mLayers)
l.onUpdate(mapPosition, changed);
}
public void destroy() {
if (mDirtyLayers)
updateLayers();
for (Layer l : mLayers){
l.destroy();
}
}
// /**
// * Gets the optional TilesOverlay class.
// * Gets the optional TilesLayer class.
// *
// * @return the tilesOverlay
// * @return the tilesLayer
// */
// public TilesOverlay getTilesOverlay() {
// return mTilesOverlay;
// public TilesLayer getTilesLayer() {
// return mTilesLayer;
// }
//
// /**
// * Sets the optional TilesOverlay class. If set, this overlay will be
// * Sets the optional TilesLayer class. If set, this overlay will be
// drawn before all other
// * overlays and will not be included in the editable list of overlays and
// can't be cleared
// * except by a subsequent call to setTilesOverlay().
// * except by a subsequent call to setTilesLayer().
// *
// * @param tilesOverlay
// * the tilesOverlay to set
// * @param tilesLayer
// * the tilesLayer to set
// */
// public void setTilesOverlay(final TilesOverlay tilesOverlay) {
// mTilesOverlay = tilesOverlay;
// public void setTilesLayer(final TilesLayer tilesLayer) {
// mTilesLayer = tilesLayer;
// }
// public void onDraw(final Canvas c, final MapView pMapView) {
// // if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
// // mTilesOverlay.draw(c, pMapView, true);
// // if ((mTilesLayer != null) && mTilesLayer.isEnabled()) {
// // mTilesLayer.draw(c, pMapView, true);
// // }
// //
// // if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
// // mTilesOverlay.draw(c, pMapView, false);
// // if ((mTilesLayer != null) && mTilesLayer.isEnabled()) {
// // mTilesLayer.draw(c, pMapView, false);
// // }
//
// for (final Overlay overlay : mOverlayList) {
// for (final Layer overlay : mLayerList) {
// if (overlay.isEnabled()) {
// overlay.draw(c, pMapView, true);
// }
// }
//
// for (final Overlay overlay : mOverlayList) {
// for (final Layer overlay : mLayerList) {
// if (overlay.isEnabled()) {
// overlay.draw(c, pMapView, false);
// }
@ -355,10 +375,10 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
// ** Options Menu **//
// public void setOptionsMenusEnabled(final boolean pEnabled) {
// for (final Overlay overlay : mOverlayList) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
// ((IOverlayMenuProvider) overlay).setOptionsMenuEnabled(pEnabled);
// for (final Layer overlay : mLayerList) {
// if ((overlay instanceof ILayerMenuProvider)
// && ((ILayerMenuProvider) overlay).isOptionsMenuEnabled()) {
// ((ILayerMenuProvider) overlay).setOptionsMenuEnabled(pEnabled);
// }
// }
// }
@ -367,19 +387,19 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
// menuIdOffset,
// final MapView mapView) {
// boolean result = true;
// for (final Overlay overlay : this.overlaysReversed()) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
// result &= ((IOverlayMenuProvider) overlay).onCreateOptionsMenu(pMenu,
// for (final Layer overlay : this.overlaysReversed()) {
// if ((overlay instanceof ILayerMenuProvider)
// && ((ILayerMenuProvider) overlay).isOptionsMenuEnabled()) {
// result &= ((ILayerMenuProvider) overlay).onCreateOptionsMenu(pMenu,
// menuIdOffset,
// mapView);
// }
// }
//
// if ((mTilesOverlay != null) && (mTilesOverlay instanceof
// IOverlayMenuProvider)
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()) {
// result &= mTilesOverlay.onCreateOptionsMenu(pMenu, menuIdOffset,
// if ((mTilesLayer != null) && (mTilesLayer instanceof
// ILayerMenuProvider)
// && ((ILayerMenuProvider) mTilesLayer).isOptionsMenuEnabled()) {
// result &= mTilesLayer.onCreateOptionsMenu(pMenu, menuIdOffset,
// mapView);
// }
//
@ -389,18 +409,18 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
// public boolean onPrepareOptionsMenu(final Menu pMenu, final int
// menuIdOffset,
// final MapView mapView) {
// for (final Overlay overlay : this.overlaysReversed()) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
// ((IOverlayMenuProvider) overlay).onPrepareOptionsMenu(pMenu,
// for (final Layer overlay : this.overlaysReversed()) {
// if ((overlay instanceof ILayerMenuProvider)
// && ((ILayerMenuProvider) overlay).isOptionsMenuEnabled()) {
// ((ILayerMenuProvider) overlay).onPrepareOptionsMenu(pMenu,
// menuIdOffset, mapView);
// }
// }
//
// if ((mTilesOverlay != null) && (mTilesOverlay instanceof
// IOverlayMenuProvider)
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()) {
// mTilesOverlay.onPrepareOptionsMenu(pMenu, menuIdOffset, mapView);
// if ((mTilesLayer != null) && (mTilesLayer instanceof
// ILayerMenuProvider)
// && ((ILayerMenuProvider) mTilesLayer).isOptionsMenuEnabled()) {
// mTilesLayer.onPrepareOptionsMenu(pMenu, menuIdOffset, mapView);
// }
//
// return true;
@ -409,20 +429,20 @@ public class LayerManager extends AbstractList<Overlay> implements OnGestureList
// public boolean onOptionsItemSelected(final MenuItem item, final int
// menuIdOffset,
// final MapView mapView) {
// for (final Overlay overlay : this.overlaysReversed()) {
// if ((overlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()
// && ((IOverlayMenuProvider) overlay).onOptionsItemSelected(item,
// for (final Layer overlay : this.overlaysReversed()) {
// if ((overlay instanceof ILayerMenuProvider)
// && ((ILayerMenuProvider) overlay).isOptionsMenuEnabled()
// && ((ILayerMenuProvider) overlay).onOptionsItemSelected(item,
// menuIdOffset,
// mapView)) {
// return true;
// }
// }
//
// if ((mTilesOverlay != null)
// && (mTilesOverlay instanceof IOverlayMenuProvider)
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()
// && ((IOverlayMenuProvider) mTilesOverlay).onOptionsItemSelected(item,
// if ((mTilesLayer != null)
// && (mTilesLayer instanceof ILayerMenuProvider)
// && ((ILayerMenuProvider) mTilesLayer).isOptionsMenuEnabled()
// && ((ILayerMenuProvider) mTilesLayer).onOptionsItemSelected(item,
// menuIdOffset,
// mapView)) {
// return true;

View File

@ -14,11 +14,8 @@
*/
package org.oscim.view;
import java.io.FileNotFoundException;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.theme.InternalRenderTheme;
import android.app.Activity;
import android.content.SharedPreferences;
@ -78,7 +75,7 @@ public abstract class MapActivity extends Activity {
editor.putInt(KEY_LONGITUDE, geoPoint.longitudeE6);
editor.putFloat(KEY_MAP_SCALE, (float)mapPosition.scale);
editor.putString(KEY_THEME, mMapView.getRenderTheme());
//editor.putString(KEY_THEME, mMapView.getRenderTheme());
editor.commit();
}
@ -121,21 +118,21 @@ public abstract class MapActivity extends Activity {
mMapView.getMapViewPosition().setMapPosition(mapPosition);
}
String theme = sharedPreferences.getString(KEY_THEME,
InternalRenderTheme.DEFAULT.name());
//String theme = sharedPreferences.getString(KEY_THEME,
// InternalRenderTheme.DEFAULT.name());
if (theme.startsWith("/")) {
try {
mapView.setRenderTheme(theme);
} catch (FileNotFoundException e) {
mapView.setRenderTheme(InternalRenderTheme.DEFAULT);
}
} else {
try {
mapView.setRenderTheme(InternalRenderTheme.valueOf(theme));
} catch (IllegalArgumentException e) {
mapView.setRenderTheme(InternalRenderTheme.DEFAULT);
}
}
// if (theme.startsWith("/")) {
// try {
// mapView.setRenderTheme(theme);
// } catch (FileNotFoundException e) {
// mapView.setRenderTheme(InternalRenderTheme.DEFAULT);
// }
// } else {
// try {
// mapView.setRenderTheme(InternalRenderTheme.valueOf(theme));
// } catch (IllegalArgumentException e) {
// mapView.setRenderTheme(InternalRenderTheme.DEFAULT);
// }
// }
}
}

View File

@ -15,41 +15,21 @@
*/
package org.oscim.view;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.database.IMapDatabase;
import org.oscim.database.MapDatabaseFactory;
import org.oscim.database.MapDatabases;
import org.oscim.database.MapInfo;
import org.oscim.database.MapOptions;
import org.oscim.database.OpenResult;
import org.oscim.generator.JobQueue;
import org.oscim.generator.JobTile;
import org.oscim.generator.MapWorker;
import org.oscim.generator.TileGenerator;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.TileGenerator;
import org.oscim.layers.tile.TileLayer;
import org.oscim.overlay.BuildingOverlay;
import org.oscim.overlay.LabelingOverlay;
import org.oscim.overlay.Overlay;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.GLView;
import org.oscim.renderer.TileManager;
import org.oscim.theme.ExternalRenderTheme;
import org.oscim.theme.InternalRenderTheme;
import org.oscim.theme.RenderTheme;
import org.oscim.theme.RenderThemeHandler;
import org.oscim.theme.Theme;
import org.oscim.utils.AndroidUtils;
import org.xml.sax.SAXException;
import android.content.Context;
import android.util.AttributeSet;
@ -68,43 +48,28 @@ public class MapView extends RelativeLayout {
public static final boolean debugFrameTime = false;
public static final boolean testRegionZoom = false;
private static final boolean debugDatabase = false;
public boolean mRotationEnabled = false;
public boolean mCompassEnabled = false;
public boolean enablePagedFling = false;
private final GLView mGLView;
private final LayerManager mLayerManager;
private final MapViewPosition mMapViewPosition;
private final MapPosition mMapPosition;
//private final MapZoomControls mMapZoomControls;
private final Compass mCompass;
private final TileManager mTileManager;
private final LayerManager mLayerManager;
final GLView mGLView;
private final JobQueue mJobQueue;
// TODO use 1 download and 1 generator thread instead
private final MapWorker mMapWorkers[];
private final int mNumMapWorkers = 4;
private MapOptions mMapOptions;
private IMapDatabase mMapDatabase;
private String mRenderTheme;
private DebugSettings mDebugSettings;
private boolean mClearMap;
private int mWidth;
private int mHeight;
// FIXME: keep until old pbmap reader is removed
public static boolean enableClosePolygons = false;
public final float dpi;
public static float dpi;
/**
* @param context
@ -152,21 +117,12 @@ public class MapView extends RelativeLayout {
mCompass = new Compass(mapActivity, this);
mJobQueue = new JobQueue();
mTileManager = new TileManager(this);
mGLView = new GLView(context, this);
mMapWorkers = new MapWorker[mNumMapWorkers];
mDebugSettings = new DebugSettings();
TileGenerator.setDebugSettings(mDebugSettings);
for (int i = 0; i < mNumMapWorkers; i++) {
TileGenerator tileGenerator = new TileGenerator();
mMapWorkers[i] = new MapWorker(i, mJobQueue, tileGenerator, mTileManager);
mMapWorkers[i].start();
}
// FIXME
TileGenerator.setDebugSettings(mDebugSettings);
mapActivity.registerMapView(this);
@ -176,7 +132,19 @@ public class MapView extends RelativeLayout {
addView(mGLView, params);
mLayerManager.add(new MapEventLayer(this));
requestRedraw();
}
public TileLayer setBaseMap(MapOptions options){
TileLayer baseLayer = new TileLayer(this);
baseLayer.setMapDatabase(options);
mLayerManager.add(0,new MapEventLayer(this));
mLayerManager.add(1,baseLayer);
//mMapZoomControls = new MapZoomControls(mapActivity, this);
//mMapZoomControls.setShowMapZoomControls(true);
@ -184,41 +152,14 @@ public class MapView extends RelativeLayout {
//mLayerManager.add(new GenericOverlay(this, new GridOverlay(this)));
mLayerManager.add(new BuildingOverlay(this));
mLayerManager.add(new LabelingOverlay(this));
mLayerManager.add(new BuildingOverlay(this, baseLayer.getTileLayer()));
mLayerManager.add(new LabelingOverlay(this, baseLayer.getTileLayer()));
//mLayerManager.add(new GenericOverlay(this, new TileOverlay(this)));
//mLayerManager.add(new GenericOverlay(this, new CustomOverlay(this)));
//mLayerManager.add(new MapLensOverlay(this));
//PathOverlay path = new PathOverlay(this, Color.RED);
//path.addGreatCircle(new GeoPoint(53.1, 8.8), new GeoPoint(53.1, -110.0));
//mLayerManager.add(path);
//path = new PathOverlay(this, Color.GREEN);
//path.addGreatCircle(new GeoPoint(53.1, 140), new GeoPoint(53.1, -110.0));
//mLayerManager.add(path);
//mLayerManager.add(new GenericOverlay(this, new AtlasTest(this)));
clearMap();
return baseLayer;
}
void destroy() {
mTileManager.destroy();
for (MapWorker mapWorker : mMapWorkers) {
mapWorker.pause();
mapWorker.interrupt();
mapWorker.getTileGenerator().getMapDatabase().close();
try {
mapWorker.join(10000);
} catch (InterruptedException e) {
// restore the interrupted status
Thread.currentThread().interrupt();
}
}
mLayerManager.destroy();
}
private boolean mPausing = false;
@ -226,18 +167,12 @@ public class MapView extends RelativeLayout {
void onPause() {
mPausing = true;
Log.d(TAG, "onPause");
mJobQueue.clear();
mapWorkersPause(true);
if (this.mCompassEnabled)
mCompass.disable();
}
void onResume() {
Log.d(TAG, "onResume");
mapWorkersProceed();
if (this.mCompassEnabled)
mCompass.enable();
@ -247,7 +182,7 @@ public class MapView extends RelativeLayout {
public void onStop() {
Log.d(TAG, "onStop");
//mTileManager.destroy();
//mLayerManager.destroy();
}
@Override
@ -264,9 +199,6 @@ public class MapView extends RelativeLayout {
int oldWidth, int oldHeight) {
Log.d(TAG, "onSizeChanged: " + width + "x" + height);
mJobQueue.clear();
mapWorkersPause(true);
super.onSizeChanged(width, height, oldWidth, oldHeight);
mWidth = width;
@ -274,9 +206,6 @@ public class MapView extends RelativeLayout {
if (width != 0 && height != 0)
mMapViewPosition.setViewport(width, height);
clearMap();
mapWorkersProceed();
}
public void render() {
@ -315,6 +244,24 @@ public class MapView extends RelativeLayout {
return mRotationEnabled;
}
boolean mWaitRedraw;
Runnable mRedrawRequest = new Runnable() {
@Override
public void run() {
mWaitRedraw = false;
redrawMap(true);
}
};
public void requestRedraw(){
if (!mWaitRedraw){
mWaitRedraw = true;
post(mRedrawRequest);
}
}
/**
* Calculates all necessary tiles and adds jobs accordingly.
*
@ -324,33 +271,27 @@ public class MapView extends RelativeLayout {
if (mPausing || mWidth == 0 || mHeight == 0)
return;
if (forceRedraw)
boolean changed = false;
if (forceRedraw){
render();
if (mClearMap) {
mTileManager.init(mWidth, mHeight);
mClearMap = false;
// make sure mMapPosition will be updated
mMapPosition.zoomLevel = -1;
// TODO clear overlays
changed = true;
}
// if (mClearMap) {
// mTileManager.init(mWidth, mHeight);
// mClearMap = false;
//
// // make sure mMapPosition will be updated
// mMapPosition.zoomLevel = -1;
//
// // TODO clear overlays
// }
boolean changed = mMapViewPosition.getMapPosition(mMapPosition);
changed |= mMapViewPosition.getMapPosition(mMapPosition);
// required when not changed?
if (AndroidUtils.currentThreadIsUiThread())
mLayerManager.onUpdate(mMapPosition, changed);
if (changed) {
mTileManager.update(mMapPosition);
}
}
private void clearMap() {
// clear tile and overlay data before next draw
mClearMap = true;
}
/**
@ -360,7 +301,7 @@ public class MapView extends RelativeLayout {
public void setDebugSettings(DebugSettings debugSettings) {
mDebugSettings = debugSettings;
TileGenerator.setDebugSettings(debugSettings);
clearMap();
//clearMap();
}
/**
@ -370,184 +311,10 @@ public class MapView extends RelativeLayout {
return mDebugSettings;
}
public Map<String, String> getMapOptions() {
return mMapOptions;
}
public MapPosition getMapFileCenter() {
if (mMapDatabase == null)
return null;
MapInfo mapInfo = mMapDatabase.getMapInfo();
if (mapInfo == null)
return null;
GeoPoint startPos = mapInfo.startPosition;
if (startPos == null)
startPos = mapInfo.mapCenter;
if (startPos == null)
startPos = new GeoPoint(0, 0);
MapPosition mapPosition = new MapPosition();
mapPosition.setPosition(startPos);
if (mapInfo.startZoomLevel == null)
mapPosition.setZoomLevel(12);
else
mapPosition.setZoomLevel((mapInfo.startZoomLevel).byteValue());
return mapPosition;
}
public void setMapPosition(MapPosition mapPosition) {
mMapViewPosition.setMapPosition(mapPosition);
}
/**
* Sets the MapDatabase for this MapView.
*
* @param options
* the new MapDatabase options.
* @return true if MapDatabase changed
*/
public boolean setMapDatabase(MapOptions options) {
if (debugDatabase)
return false;
Log.i(TAG, "setMapDatabase: " + options.db.name());
if (mMapOptions != null && mMapOptions.equals(options))
return true;
mapWorkersPause(true);
mJobQueue.clear();
mMapOptions = options;
mMapDatabase = null;
for (int i = 0; i < mNumMapWorkers; i++) {
MapWorker mapWorker = mMapWorkers[i];
IMapDatabase mapDatabase = MapDatabaseFactory
.createMapDatabase(options.db);
OpenResult result = mapDatabase.open(options);
if (result != OpenResult.SUCCESS) {
Log.d(TAG, "failed open db: " + result.getErrorMessage());
}
TileGenerator tileGenerator = mapWorker.getTileGenerator();
tileGenerator.setMapDatabase(mapDatabase);
// TODO this could be done in a cleaner way..
if (mMapDatabase == null)
mMapDatabase = mapDatabase;
}
if (options.db == MapDatabases.OSCIMAP_READER ||
options.db == MapDatabases.MAP_READER)
MapView.enableClosePolygons = true;
else
MapView.enableClosePolygons = false;
clearMap();
mapWorkersProceed();
return true;
}
public String getRenderTheme() {
return mRenderTheme;
}
/**
* Sets the internal theme which is used for rendering the map.
*
* @param internalRenderTheme
* the internal rendering theme.
* @return ...
* @throws IllegalArgumentException
* if the supplied internalRenderTheme is null.
*/
public boolean setRenderTheme(InternalRenderTheme internalRenderTheme) {
if (internalRenderTheme == null) {
throw new IllegalArgumentException("render theme must not be null");
}
if (internalRenderTheme.name() == mRenderTheme)
return true;
boolean ret = setRenderTheme((Theme) internalRenderTheme);
if (ret) {
mRenderTheme = internalRenderTheme.name();
}
clearMap();
return ret;
}
/**
* Sets the theme file which is used for rendering the map.
*
* @param renderThemePath
* the path to the XML file which defines the rendering theme.
* @throws IllegalArgumentException
* if the supplied internalRenderTheme is null.
* @throws FileNotFoundException
* if the supplied file does not exist, is a directory or cannot
* be read.
*/
public void setRenderTheme(String renderThemePath) throws FileNotFoundException {
if (renderThemePath == null) {
throw new IllegalArgumentException("render theme path must not be null");
}
boolean ret = setRenderTheme(new ExternalRenderTheme(renderThemePath));
if (ret) {
mRenderTheme = renderThemePath;
}
clearMap();
}
private boolean setRenderTheme(Theme theme) {
mapWorkersPause(true);
InputStream inputStream = null;
try {
inputStream = theme.getRenderThemeAsStream();
RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream);
t.scaleTextSize(1 + (dpi / 240 - 1) * 0.5f);
// FIXME
GLRenderer.setRenderTheme(t);
TileGenerator.setRenderTheme(t);
return true;
} catch (ParserConfigurationException e) {
Log.e(TAG, e.getMessage());
} catch (SAXException e) {
Log.e(TAG, e.getMessage());
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
mapWorkersProceed();
}
return false;
}
/**
* Sets the center of the MapView and triggers a redraw.
*
@ -567,45 +334,6 @@ public class MapView extends RelativeLayout {
return mMapViewPosition;
}
/**
* add jobs and remember MapWorkers that stuff needs to be done
*
* @param jobs
* tile jobs
*/
public void addJobs(JobTile[] jobs) {
if (jobs == null) {
mJobQueue.clear();
return;
}
mJobQueue.setJobs(jobs);
for (int i = 0; i < mNumMapWorkers; i++) {
MapWorker m = mMapWorkers[i];
synchronized (m) {
m.notify();
}
}
}
private void mapWorkersPause(boolean wait) {
for (MapWorker mapWorker : mMapWorkers) {
if (!mapWorker.isPausing())
mapWorker.pause();
}
if (wait) {
for (MapWorker mapWorker : mMapWorkers) {
if (!mapWorker.isPausing())
mapWorker.awaitPausing();
}
}
}
private void mapWorkersProceed() {
for (MapWorker mapWorker : mMapWorkers)
mapWorker.proceed();
}
/**
* You can add/remove/reorder your Overlays using the List of
* {@link Overlay}. The first (index 0) Overlay gets drawn first, the one
@ -613,7 +341,7 @@ public class MapView extends RelativeLayout {
*
* @return ...
*/
public List<Overlay> getOverlays() {
public List<Layer> getOverlays() {
return this.getOverlayManager();
}
@ -621,10 +349,6 @@ public class MapView extends RelativeLayout {
return mLayerManager;
}
public TileManager getTileManager() {
return mTileManager;
}
/**
* @return estimated visible axis aligned bounding box
*/