add mapsforge-map android part to VectorTileMap
This commit is contained in:
254
src/org/mapsforge/android/swrenderer/CanvasRasterer.java
Normal file
254
src/org/mapsforge/android/swrenderer/CanvasRasterer.java
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mapsforge.core.Tile;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Typeface;
|
||||
|
||||
/**
|
||||
* A CanvasRasterer uses a Canvas for drawing.
|
||||
*
|
||||
* @see <a href="http://developer.android.com/reference/android/graphics/Canvas.html">Canvas</a>
|
||||
*/
|
||||
class CanvasRasterer {
|
||||
private static final Paint PAINT_BITMAP_FILTER = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||
private static final Paint PAINT_TILE_COORDINATES = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final Paint PAINT_TILE_COORDINATES_STROKE = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final Paint PAINT_TILE_FRAME = new Paint();
|
||||
|
||||
private static final Paint PAINT_MARK = new Paint();
|
||||
static final int COLOR_MARK = Color.argb(30, 0, 255, 0);
|
||||
|
||||
// private static final float[] TILE_FRAME = new float[] { 0, 0, 0, Tile.TILE_SIZE, 0, Tile.TILE_SIZE,
|
||||
// Tile.TILE_SIZE,
|
||||
// Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0 };
|
||||
|
||||
private static void configurePaints() {
|
||||
PAINT_TILE_COORDINATES.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
PAINT_TILE_COORDINATES.setTextSize(12);
|
||||
|
||||
PAINT_TILE_COORDINATES_STROKE.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
PAINT_TILE_COORDINATES_STROKE.setStyle(Paint.Style.STROKE);
|
||||
PAINT_TILE_COORDINATES_STROKE.setStrokeWidth(1);
|
||||
PAINT_TILE_COORDINATES_STROKE.setTextSize(6);
|
||||
PAINT_TILE_COORDINATES_STROKE.setColor(Color.WHITE);
|
||||
PAINT_MARK.setColor(COLOR_MARK);
|
||||
|
||||
}
|
||||
|
||||
private final Canvas mCanvas;
|
||||
private final Path mPath;
|
||||
private final Matrix mSymbolMatrix;
|
||||
|
||||
private float mScaleFactor;
|
||||
|
||||
CanvasRasterer() {
|
||||
mCanvas = new Canvas();
|
||||
mSymbolMatrix = new Matrix();
|
||||
mPath = new Path();
|
||||
mPath.setFillType(Path.FillType.EVEN_ODD);
|
||||
mScaleFactor = 1;
|
||||
configurePaints();
|
||||
|
||||
}
|
||||
|
||||
private void drawTileCoordinate(String string, int offsetY) {
|
||||
mCanvas.drawText(string, 20, offsetY, PAINT_TILE_COORDINATES);
|
||||
}
|
||||
|
||||
void drawNodes(List<PointTextContainer> pointTextContainers) {
|
||||
|
||||
for (int index = pointTextContainers.size() - 1; index >= 0; --index) {
|
||||
PointTextContainer pointTextContainer = pointTextContainers.get(index);
|
||||
|
||||
if (pointTextContainer.paintBack != null) {
|
||||
|
||||
mCanvas.drawText(pointTextContainer.text, pointTextContainer.x * mScaleFactor, pointTextContainer.y
|
||||
* mScaleFactor, pointTextContainer.paintBack);
|
||||
}
|
||||
|
||||
mCanvas.drawText(pointTextContainer.text, pointTextContainer.x * mScaleFactor, pointTextContainer.y
|
||||
* mScaleFactor, pointTextContainer.paintFront);
|
||||
}
|
||||
}
|
||||
|
||||
void drawSymbols(List<SymbolContainer> symbolContainers) {
|
||||
for (int index = symbolContainers.size() - 1; index >= 0; --index) {
|
||||
SymbolContainer symbolContainer = symbolContainers.get(index);
|
||||
|
||||
if (symbolContainer.alignCenter) {
|
||||
int pivotX = symbolContainer.symbol.getWidth() >> 1;
|
||||
int pivotY = symbolContainer.symbol.getHeight() >> 1;
|
||||
mSymbolMatrix.setRotate(symbolContainer.rotation, pivotX, pivotY);
|
||||
mSymbolMatrix.postTranslate(symbolContainer.x - pivotX, symbolContainer.y - pivotY);
|
||||
} else {
|
||||
mSymbolMatrix.setRotate(symbolContainer.rotation);
|
||||
mSymbolMatrix.postTranslate(symbolContainer.x, symbolContainer.y);
|
||||
}
|
||||
mSymbolMatrix.postTranslate(mScaleFactor, mScaleFactor);
|
||||
|
||||
// symbolMatrix.postScale(zoomFactor, zoomFactor);
|
||||
mCanvas.drawBitmap(symbolContainer.symbol, mSymbolMatrix, PAINT_BITMAP_FILTER);
|
||||
}
|
||||
}
|
||||
|
||||
void drawTileCoordinates(Tile tile, long time_load, long time_draw, long blub, long blah) {
|
||||
|
||||
drawTileCoordinate(tile.tileX + " / " + tile.tileY + " / " + tile.zoomLevel + " " + mScaleFactor, 20);
|
||||
|
||||
drawTileCoordinate("l:" + time_load, 40);
|
||||
drawTileCoordinate("d:" + time_draw, 60);
|
||||
drawTileCoordinate("+:" + blub, 80);
|
||||
drawTileCoordinate("-:" + blah, 100);
|
||||
|
||||
}
|
||||
|
||||
void drawTileFrame() {
|
||||
float size = (Tile.TILE_SIZE * mScaleFactor);
|
||||
float[] frame = new float[] { 0, 0, 0, size - 1, 0, size - 1, size - 1, size - 1, size - 1, size - 1, size - 1,
|
||||
0 };
|
||||
mCanvas.drawLines(frame, PAINT_TILE_FRAME);
|
||||
}
|
||||
|
||||
void drawWayNames(float[] coords, List<WayTextContainer> wayTextContainers) {
|
||||
|
||||
for (int index = wayTextContainers.size() - 1; index >= 0; --index) {
|
||||
WayTextContainer wayTextContainer = wayTextContainers.get(index);
|
||||
mPath.rewind();
|
||||
|
||||
int first = wayTextContainer.first;
|
||||
int last = wayTextContainer.last;
|
||||
|
||||
// int len = wayTextContainer.wayDataContainer.length[0];
|
||||
// int pos = wayTextContainer.wayDataContainer.position[0];
|
||||
|
||||
// System.arraycopy(floats, pos, coords, 0, len);
|
||||
|
||||
if (coords[first] < coords[last]) {
|
||||
mPath.moveTo(coords[first], coords[first + 1]);
|
||||
|
||||
for (int i = first + 2; i <= last; i += 2) {
|
||||
mPath.lineTo(coords[i], coords[i + 1]);
|
||||
}
|
||||
} else {
|
||||
mPath.moveTo(coords[last], coords[last + 1]);
|
||||
|
||||
for (int i = last - 2; i >= first; i -= 2) {
|
||||
mPath.lineTo(coords[i], coords[i + 1]);
|
||||
}
|
||||
}
|
||||
mCanvas.drawTextOnPath(wayTextContainer.text, mPath, 0, 3, wayTextContainer.paint);
|
||||
|
||||
// if (wayTextContainer.match)
|
||||
// canvas.drawRect(wayTextContainer.x1,
|
||||
// wayTextContainer.top, wayTextContainer.x2,
|
||||
// wayTextContainer.bot, PAINT_MARK);
|
||||
}
|
||||
}
|
||||
|
||||
void drawWays(float[] coords, LayerContainer[] drawWays) {
|
||||
int levels = drawWays[0].mLevelActive.length;
|
||||
|
||||
for (LayerContainer layerContainer : drawWays) {
|
||||
if (!layerContainer.mActive)
|
||||
continue;
|
||||
|
||||
for (int level = 0; level < levels; level++) {
|
||||
|
||||
if (!layerContainer.mLevelActive[level])
|
||||
continue;
|
||||
|
||||
// mPath.rewind();
|
||||
|
||||
LevelContainer levelContainer = layerContainer.mLevels[level];
|
||||
|
||||
for (int way = levelContainer.mShapeContainers.size() - 1; way >= 0; way--) {
|
||||
mPath.rewind();
|
||||
// switch (shapePaintContainer.shapeContainer.getShapeType()) {
|
||||
//
|
||||
// case WAY:
|
||||
WayDataContainer wayDataContainer = (WayDataContainer) levelContainer.mShapeContainers.get(way);
|
||||
// (WayDataContainer) shapePaintContainer.shapeContainer;
|
||||
|
||||
// if (wayDataContainer.closed) {
|
||||
for (int i = 0, n = wayDataContainer.length.length; i < n; i++) {
|
||||
|
||||
int len = wayDataContainer.length[i];
|
||||
int pos = wayDataContainer.position[i];
|
||||
if (len > 2) {
|
||||
mPath.moveTo(coords[pos], coords[pos + 1]);
|
||||
|
||||
for (int j = pos + 2; j < len + pos; j += 2)
|
||||
mPath.lineTo(coords[j], coords[j + 1]);
|
||||
}
|
||||
}
|
||||
mCanvas.drawPath(mPath, levelContainer.mPaint[0]);
|
||||
if (levelContainer.mPaint[1] != null)
|
||||
mCanvas.drawPath(mPath, levelContainer.mPaint[1]);
|
||||
|
||||
// }else {
|
||||
// for (int i = 0, n = wayDataContainer.length.length; i < n; i++) {
|
||||
// // levelContainer.mPaint[0].setStrokeJoin(Join.ROUND);
|
||||
//
|
||||
// int len = wayDataContainer.length[i];
|
||||
// int pos = wayDataContainer.position[i];
|
||||
// if (len > 2) {
|
||||
// mCanvas.drawPoints(coords, pos, len, levelContainer.mPaint[0]);
|
||||
// if (levelContainer.mPaint[1] != null)
|
||||
// mCanvas.drawPoints(coords, pos, len, levelContainer.mPaint[1]);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
|
||||
// case CIRCLE:
|
||||
// CircleContainer circleContainer =
|
||||
// (CircleContainer) shapePaintContainer.shapeContainer;
|
||||
//
|
||||
// mPath.rewind();
|
||||
//
|
||||
// mPath.addCircle(circleContainer.mX, circleContainer.mY,
|
||||
// circleContainer.mRadius, Path.Direction.CCW);
|
||||
//
|
||||
// mCanvas.drawPath(mPath, shapePaintContainer.paint);
|
||||
// break;
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fill(int color) {
|
||||
mCanvas.drawColor(color);
|
||||
}
|
||||
|
||||
void setCanvasBitmap(Bitmap bitmap, float scale) {
|
||||
mCanvas.setBitmap(bitmap);
|
||||
// add some extra pixels to avoid < 1px blank edges while scaling
|
||||
mCanvas.clipRect(0, 0, Tile.TILE_SIZE * scale + 2, Tile.TILE_SIZE * scale + 2);
|
||||
mScaleFactor = scale;
|
||||
}
|
||||
}
|
||||
33
src/org/mapsforge/android/swrenderer/CircleContainer.java
Normal file
33
src/org/mapsforge/android/swrenderer/CircleContainer.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
|
||||
class CircleContainer implements ShapeContainer {
|
||||
final float mRadius;
|
||||
final float mX;
|
||||
final float mY;
|
||||
|
||||
CircleContainer(float x, float y, float radius) {
|
||||
mX = x;
|
||||
mY = y;
|
||||
mRadius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType getShapeType() {
|
||||
return ShapeType.CIRCLE;
|
||||
}
|
||||
}
|
||||
581
src/org/mapsforge/android/swrenderer/DatabaseRenderer.java
Normal file
581
src/org/mapsforge/android/swrenderer/DatabaseRenderer.java
Normal file
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.mapsforge.android.MapView;
|
||||
import org.mapsforge.android.mapgenerator.JobTheme;
|
||||
import org.mapsforge.android.mapgenerator.MapGenerator;
|
||||
import org.mapsforge.android.mapgenerator.MapGeneratorJob;
|
||||
import org.mapsforge.android.rendertheme.RenderCallback;
|
||||
import org.mapsforge.android.rendertheme.RenderTheme;
|
||||
import org.mapsforge.android.rendertheme.RenderThemeHandler;
|
||||
import org.mapsforge.android.rendertheme.renderinstruction.Area;
|
||||
import org.mapsforge.android.rendertheme.renderinstruction.Line;
|
||||
import org.mapsforge.core.GeoPoint;
|
||||
import org.mapsforge.core.Tag;
|
||||
import org.mapsforge.core.Tile;
|
||||
import org.mapsforge.mapdatabase.IMapDatabase;
|
||||
import org.mapsforge.mapdatabase.IMapDatabaseCallback;
|
||||
import org.mapsforge.mapdatabase.MapFileInfo;
|
||||
import org.mapsforge.mapdatabase.mapfile.MapDatabase;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A DatabaseRenderer renders map tiles by reading from a {@link MapDatabase}.
|
||||
*/
|
||||
public class DatabaseRenderer implements MapGenerator, RenderCallback, IMapDatabaseCallback {
|
||||
private static String TAG = DatabaseRenderer.class.getName();
|
||||
private static final Byte DEFAULT_START_ZOOM_LEVEL = Byte.valueOf((byte) 12);
|
||||
private static final byte LAYERS = 11;
|
||||
private static final Paint PAINT_WATER_TILE_HIGHTLIGHT = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final double STROKE_INCREASE = 1.5;
|
||||
private static final byte STROKE_MIN_ZOOM_LEVEL = 12;
|
||||
|
||||
private static final byte ZOOM_MAX = 22;
|
||||
|
||||
// private static MapRenderer mMapRenderer;
|
||||
|
||||
private static RenderTheme getRenderTheme(JobTheme jobTheme) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = jobTheme.getRenderThemeAsStream();
|
||||
return RenderThemeHandler.getRenderTheme(inputStream);
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte getValidLayer(byte layer) {
|
||||
if (layer < 0) {
|
||||
return 0;
|
||||
} else if (layer >= LAYERS) {
|
||||
return LAYERS - 1;
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
|
||||
private final CanvasRasterer mCanvasRasterer;
|
||||
|
||||
private LayerContainer mDrawingLayer;
|
||||
private final LabelPlacement mLabelPlacement;
|
||||
private IMapDatabase mMapDatabase;
|
||||
private List<PointTextContainer> mNodes;
|
||||
private float mPoiX;
|
||||
private float mPoiY;
|
||||
private JobTheme mPreviousJobTheme;
|
||||
private float mPreviousTextScale;
|
||||
private byte mPreviousZoomLevel;
|
||||
private static RenderTheme renderTheme;
|
||||
|
||||
private final List<WayTextContainer> mWayNames;
|
||||
private final LayerContainer[] mWays;
|
||||
private final List<SymbolContainer> mWaySymbols;
|
||||
private final List<SymbolContainer> mPointSymbols;
|
||||
private final List<PointTextContainer> mAreaLabels;
|
||||
|
||||
// private float mLat1, mLat2, mLon1, mLon2;
|
||||
// private float mTileWidth, mTileHeight;
|
||||
private float mScale;
|
||||
|
||||
// private float[] mCoordinates;
|
||||
private WayDataContainer mWayDataContainer;
|
||||
private final Bitmap mTileBitmap;
|
||||
|
||||
private static float PI180 = (float) (Math.PI / 180) / 1000000.0f;
|
||||
private static float PIx4 = (float) Math.PI * 4;
|
||||
|
||||
private Tile mCurrentTile;
|
||||
private static long mCurrentTileY;
|
||||
private static long mCurrentTileX;
|
||||
private static long mCurrentTileZoom;
|
||||
|
||||
private float[] mCoords = null;
|
||||
|
||||
// private long _renderTime;
|
||||
private int _nodes, _nodesDropped;
|
||||
|
||||
/**
|
||||
* Constructs a new DatabaseRenderer.
|
||||
*/
|
||||
public DatabaseRenderer() {
|
||||
mCanvasRasterer = new CanvasRasterer();
|
||||
mLabelPlacement = new LabelPlacement();
|
||||
|
||||
mWays = new LayerContainer[LAYERS];
|
||||
mWayNames = new ArrayList<WayTextContainer>(64);
|
||||
mNodes = new ArrayList<PointTextContainer>(64);
|
||||
mAreaLabels = new ArrayList<PointTextContainer>(64);
|
||||
mWaySymbols = new ArrayList<SymbolContainer>(64);
|
||||
mPointSymbols = new ArrayList<SymbolContainer>(64);
|
||||
|
||||
PAINT_WATER_TILE_HIGHTLIGHT.setStyle(Paint.Style.FILL);
|
||||
PAINT_WATER_TILE_HIGHTLIGHT.setColor(Color.CYAN);
|
||||
// mCoordinates = new float[1024];
|
||||
|
||||
mTileBitmap = Bitmap.createBitmap(Tile.TILE_SIZE * 2, Tile.TILE_SIZE * 2, Bitmap.Config.RGB_565);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
mTileBitmap.recycle();
|
||||
if (DatabaseRenderer.renderTheme != null) {
|
||||
DatabaseRenderer.renderTheme.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeJob(MapGeneratorJob mapGeneratorJob) {
|
||||
long time_load = System.currentTimeMillis();
|
||||
_nodes = 0;
|
||||
_nodesDropped = 0;
|
||||
// _renderTime = 0;
|
||||
|
||||
mCurrentTile = mapGeneratorJob.tile;
|
||||
mCurrentTileZoom = ((long) Tile.TILE_SIZE << mCurrentTile.zoomLevel);
|
||||
mCurrentTileX = mCurrentTile.pixelX;
|
||||
mCurrentTileY = mCurrentTile.pixelY;
|
||||
|
||||
// mLon1 = (float) MercatorProjection.pixelXToLongitude(mCurrentTileX, mCurrentTile.zoomLevel) * 1000000;
|
||||
// mLat1 = (float) MercatorProjection.pixelYToLatitude(mCurrentTileY, mCurrentTile.zoomLevel) * 1000000;
|
||||
// mLon2 = (float) MercatorProjection.pixelXToLongitude(mCurrentTileX + Tile.TILE_SIZE, mCurrentTile.zoomLevel)
|
||||
// * 1000000;
|
||||
// mLat2 = (float) MercatorProjection.pixelYToLatitude(mCurrentTileY + Tile.TILE_SIZE, mCurrentTile.zoomLevel) *
|
||||
// 1000000;
|
||||
//
|
||||
// mTileWidth = mLon2 - mLon1;
|
||||
// mTileHeight = mLat1 - mLat2;
|
||||
mScale = mapGeneratorJob.getScale();
|
||||
|
||||
JobTheme jobTheme = mapGeneratorJob.jobParameters.jobTheme;
|
||||
if (!jobTheme.equals(mPreviousJobTheme)) {
|
||||
if (DatabaseRenderer.renderTheme == null)
|
||||
DatabaseRenderer.renderTheme = getRenderTheme(jobTheme);
|
||||
if (DatabaseRenderer.renderTheme == null) {
|
||||
mPreviousJobTheme = null;
|
||||
return false;
|
||||
}
|
||||
createWayLists();
|
||||
mPreviousJobTheme = jobTheme;
|
||||
mPreviousZoomLevel = Byte.MIN_VALUE;
|
||||
}
|
||||
|
||||
byte zoomLevel = mCurrentTile.zoomLevel;
|
||||
if (zoomLevel != mPreviousZoomLevel) {
|
||||
setScaleStrokeWidth(zoomLevel);
|
||||
mPreviousZoomLevel = zoomLevel;
|
||||
}
|
||||
|
||||
float textScale = mapGeneratorJob.jobParameters.textScale;
|
||||
if (textScale != mPreviousTextScale) {
|
||||
DatabaseRenderer.renderTheme.scaleTextSize(textScale);
|
||||
mPreviousTextScale = textScale;
|
||||
}
|
||||
|
||||
if (mMapDatabase != null) {
|
||||
mMapDatabase.executeQuery(mCurrentTile, this);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
time_load = System.currentTimeMillis() - time_load;
|
||||
|
||||
mNodes = mLabelPlacement.placeLabels(mNodes, mPointSymbols, mAreaLabels, mCurrentTile);
|
||||
|
||||
long time_draw = System.currentTimeMillis();
|
||||
|
||||
// FIXME mCoords = mMapDatabase.getCoordinates();
|
||||
|
||||
mCanvasRasterer.setCanvasBitmap(mTileBitmap, mScale);
|
||||
mCanvasRasterer.fill(DatabaseRenderer.renderTheme.getMapBackground());
|
||||
mCanvasRasterer.drawWays(mCoords, mWays);
|
||||
mCanvasRasterer.drawSymbols(mWaySymbols);
|
||||
mCanvasRasterer.drawSymbols(mPointSymbols);
|
||||
mCanvasRasterer.drawWayNames(mCoords, mWayNames);
|
||||
mCanvasRasterer.drawNodes(mNodes);
|
||||
mCanvasRasterer.drawNodes(mAreaLabels);
|
||||
time_draw = System.currentTimeMillis() - time_draw;
|
||||
|
||||
if (mapGeneratorJob.debugSettings.mDrawTileFrames) {
|
||||
mCanvasRasterer.drawTileFrame();
|
||||
}
|
||||
|
||||
if (mapGeneratorJob.debugSettings.mDrawTileCoordinates) {
|
||||
mCanvasRasterer.drawTileCoordinates(mCurrentTile, time_load, time_draw, _nodes, _nodesDropped);
|
||||
}
|
||||
|
||||
clearLists();
|
||||
|
||||
mapGeneratorJob.setBitmap(mTileBitmap);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getStartPoint() {
|
||||
if (mMapDatabase != null && mMapDatabase.hasOpenFile()) {
|
||||
MapFileInfo mapFileInfo = mMapDatabase.getMapFileInfo();
|
||||
if (mapFileInfo.startPosition != null) {
|
||||
return mapFileInfo.startPosition;
|
||||
} else if (mapFileInfo.mapCenter != null) {
|
||||
return mapFileInfo.mapCenter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getStartZoomLevel() {
|
||||
if (mMapDatabase != null && mMapDatabase.hasOpenFile()) {
|
||||
MapFileInfo mapFileInfo = mMapDatabase.getMapFileInfo();
|
||||
if (mapFileInfo.startZoomLevel != null) {
|
||||
return mapFileInfo.startZoomLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return DEFAULT_START_ZOOM_LEVEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getZoomLevelMax() {
|
||||
return ZOOM_MAX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaCaption(String textKey, float verticalOffset, Paint paint, Paint stroke) {
|
||||
// mapDatabase.readTag(caption);
|
||||
// if (caption.value != null) {
|
||||
// float[] centerPosition = GeometryUtils
|
||||
// .calculateCenterOfBoundingBox(coordinates[0]);
|
||||
// areaLabels.add(new PointTextContainer(caption.value,
|
||||
// centerPosition[0],
|
||||
// centerPosition[1],
|
||||
// paint, stroke));
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaSymbol(Bitmap symbol) {
|
||||
// float[] centerPosition = GeometryUtils
|
||||
// .calculateCenterOfBoundingBox(coordinates[0]);
|
||||
// pointSymbols.add(new SymbolContainer(symbol, centerPosition[0]
|
||||
// - (symbol.getWidth() >> 1), centerPosition[1]
|
||||
// - (symbol.getHeight() >> 1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterest(byte layer, int latitude, int longitude, Tag[] tags) {
|
||||
mDrawingLayer = mWays[getValidLayer(layer)];
|
||||
mPoiX = scaleLongitude(longitude);
|
||||
mPoiY = scaleLatitude(latitude);
|
||||
DatabaseRenderer.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCaption(String textKey, float verticalOffset, Paint paint, Paint stroke) {
|
||||
// mapDatabase.readTag(caption);
|
||||
// if (caption.value != null) {
|
||||
// nodes.add(new PointTextContainer(caption.value, poiX, poiY + verticalOffset, paint, stroke));
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCircle(float radius, Paint outline, int level) {
|
||||
|
||||
mDrawingLayer.add(level, new CircleContainer(mPoiX, mPoiY, radius), outline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestSymbol(Bitmap symbol) {
|
||||
mPointSymbols.add(new SymbolContainer(symbol, mPoiX - (symbol.getWidth() >> 1), mPoiY
|
||||
- (symbol.getHeight() >> 1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaterBackground() {
|
||||
// if (mCoords == null)
|
||||
// mCoords = mMapDatabase.getCoordinates();
|
||||
|
||||
// float[] coords = mCoords;
|
||||
//
|
||||
// mDrawingLayer = mWays[5];
|
||||
//
|
||||
// int len = wayData.length[0];
|
||||
// int pos = wayData.position[0];
|
||||
//
|
||||
// for (int j = pos, m = pos + len; j < m; j += 2) {
|
||||
// coords[j] = coords[j] * mScale;
|
||||
// coords[j + 1] = coords[j + 1] * mScale;
|
||||
// }
|
||||
//
|
||||
// mWayDataContainer = wayData;
|
||||
//
|
||||
// Log.i("mapsforge", "render water");
|
||||
//
|
||||
// DatabaseRenderer.renderTheme.matchWay(this, tags, mCurrentTile.zoomLevel, true);
|
||||
}
|
||||
|
||||
// private boolean mPrevClosed = false;
|
||||
// private byte mPrevLayer = 0;
|
||||
|
||||
@Override
|
||||
public void renderWay(byte layer, Tag[] tags, float[] wayNodes, int[] wayLengths, boolean changed) {
|
||||
// if (mCoords == null)
|
||||
// mCoords = mMapDatabase.getCoordinates();
|
||||
|
||||
// float[] coords = mCoords;
|
||||
//
|
||||
// boolean closed = false;
|
||||
// boolean added = false;
|
||||
//
|
||||
// // coordinatesLength = wayData.length.length;
|
||||
// if (mCurrentTile.zoomLevel < 6) {
|
||||
// long x = mCurrentTileX;
|
||||
// long y = mCurrentTileY;
|
||||
// long z = mCurrentTileZoom;
|
||||
// float s = mScale;
|
||||
//
|
||||
// added = true;
|
||||
//
|
||||
// for (int i = wayData.length.length - 1; i >= 0; i--) {
|
||||
// int len = wayData.length[i];
|
||||
// int pos = wayData.position[i];
|
||||
//
|
||||
// if (i == 0)
|
||||
// closed = (coords[pos] == coords[(pos + len) - 2] &&
|
||||
// coords[pos + 1] == coords[(pos + len) - 1]);
|
||||
//
|
||||
// for (int j = pos, m = pos + len; j < m; j += 2) {
|
||||
//
|
||||
// coords[j] = (float) (((coords[j] / 1000000.0 + 180) / 360 * z) - x) * s;
|
||||
//
|
||||
// double sinLat = Math.sin(coords[j + 1] * PI180);
|
||||
// coords[j + 1] = (float) ((0.5 - Math.log((1 + sinLat) / (1 - sinLat)) / PIx4) * z - y) * s;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // use linear approximation on high zoom levels.
|
||||
// float ssize = Tile.TILE_SIZE * mScale;
|
||||
// float sw = ssize / mTileWidth;
|
||||
// float sh = ssize / mTileHeight;
|
||||
// int j, o;
|
||||
// float x, y;
|
||||
//
|
||||
// int min = 1;
|
||||
// if (mCurrentTile.zoomLevel < 14)
|
||||
// min = 3;
|
||||
// else if (mCurrentTile.zoomLevel < 9)
|
||||
// min = 5;
|
||||
//
|
||||
// for (int i = wayData.length.length - 1; i >= 0; i--) {
|
||||
//
|
||||
// int len = wayData.length[i];
|
||||
// int pos = wayData.position[i];
|
||||
// _nodes += len / 2;
|
||||
//
|
||||
// if (i == 0) {
|
||||
// closed = (coords[pos] == coords[(pos + len) - 2] &&
|
||||
// coords[pos + 1] == coords[(pos + len) - 1]);
|
||||
// }
|
||||
//
|
||||
// coords[pos] = (coords[pos] - mLon1) * sw;
|
||||
// coords[pos + 1] = ssize - (coords[pos + 1] - mLat2) * sh;
|
||||
//
|
||||
// j = o = pos + 2;
|
||||
//
|
||||
// // drop intermediate nodes with less than 'min' distance.
|
||||
// for (int m = pos + len - 2; j < m; j += 2) {
|
||||
// x = (coords[j] - mLon1) * sw;
|
||||
// y = ssize - (coords[j + 1] - mLat2) * sh;
|
||||
//
|
||||
// if (x > coords[o - 2] + min || x < coords[o - 2] - min ||
|
||||
// y > coords[o - 1] + min || y < coords[o - 1] - min) {
|
||||
//
|
||||
// coords[o++] = x;
|
||||
// coords[o++] = y;
|
||||
// } else
|
||||
// _nodesDropped++;
|
||||
// }
|
||||
// coords[o] = (coords[j] - mLon1) * sw;
|
||||
// coords[o + 1] = ssize - (coords[j + 1] - mLat2) * sh;
|
||||
// o += 2;
|
||||
//
|
||||
// wayData.length[i] = o - pos;
|
||||
//
|
||||
// if (!closed || (o - pos) > 4)
|
||||
// added = true;
|
||||
// else
|
||||
// wayData.length[i] = 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!added && !changed)
|
||||
// return;
|
||||
//
|
||||
// mWayDataContainer = wayData;
|
||||
//
|
||||
// mDrawingLayer = mWays[getValidLayer(layer)];
|
||||
//
|
||||
// if (changed || (closed != mPrevClosed) || (layer != mPrevLayer)) {
|
||||
// mCurLevelContainer1 = null;
|
||||
// mCurLevelContainer2 = null;
|
||||
// DatabaseRenderer.renderTheme.matchWay(this, tags, mCurrentTile.zoomLevel, closed);
|
||||
// } else {
|
||||
// if (mCurLevelContainer1 != null)
|
||||
// mCurLevelContainer1.add(mWayDataContainer);
|
||||
// if (mCurLevelContainer2 != null)
|
||||
// mCurLevelContainer2.add(mWayDataContainer);
|
||||
// }
|
||||
// mPrevClosed = closed;
|
||||
// mPrevLayer = layer;
|
||||
}
|
||||
|
||||
private List<ShapeContainer> mCurLevelContainer1;
|
||||
private List<ShapeContainer> mCurLevelContainer2;
|
||||
|
||||
@Override
|
||||
public void renderWay(Line line) {
|
||||
List<ShapeContainer> c = mDrawingLayer.add(line.level, mWayDataContainer, line.paint);
|
||||
|
||||
if (mCurLevelContainer1 == null)
|
||||
mCurLevelContainer1 = c;
|
||||
else if (mCurLevelContainer2 == null)
|
||||
mCurLevelContainer2 = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderArea(Area area) {
|
||||
if (area.paintFill != null)
|
||||
mCurLevelContainer1 = mDrawingLayer.add(area.level, mWayDataContainer, area.paintFill);
|
||||
if (area.paintOutline != null)
|
||||
mCurLevelContainer1 = mDrawingLayer.add(area.level, mWayDataContainer, area.paintOutline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaySymbol(Bitmap symbolBitmap, boolean alignCenter, boolean repeatSymbol) {
|
||||
// WayDecorator.renderSymbol(symbolBitmap, alignCenter, repeatSymbol,
|
||||
// coordinates,
|
||||
// waySymbols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWayText(String textKey, Paint paint, Paint outline) {
|
||||
// if (mWayDataContainer.textPos[0] >= 0)
|
||||
// WayDecorator.renderText(this, paint, outline, mCoords, mWayDataContainer, mWayNames);
|
||||
}
|
||||
|
||||
String getWayName() {
|
||||
return mMapDatabase.readString(mWayDataContainer.textPos[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresInternetConnection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMapDatabase(IMapDatabase mapDatabase) {
|
||||
mMapDatabase = mapDatabase;
|
||||
}
|
||||
|
||||
private void clearLists() {
|
||||
for (int i = LAYERS - 1; i >= 0; --i) {
|
||||
mWays[i].clear();
|
||||
}
|
||||
|
||||
mAreaLabels.clear();
|
||||
mNodes.clear();
|
||||
mPointSymbols.clear();
|
||||
mWayNames.clear();
|
||||
mWaySymbols.clear();
|
||||
}
|
||||
|
||||
private void createWayLists() {
|
||||
int levels = DatabaseRenderer.renderTheme.getLevels();
|
||||
for (byte i = LAYERS - 1; i >= 0; --i) {
|
||||
mWays[i] = new LayerContainer(levels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a latitude value into an Y coordinate on the current tile.
|
||||
*
|
||||
* @param latitude
|
||||
* the latitude value.
|
||||
* @return the Y coordinate on the current tile.
|
||||
*/
|
||||
private static float scaleLatitude(float latitude) {
|
||||
double sinLatitude = FloatMath.sin(latitude * PI180);
|
||||
|
||||
return (float) (0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / PIx4) * mCurrentTileZoom
|
||||
- mCurrentTileY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a longitude value into an X coordinate on the current tile.
|
||||
*
|
||||
* @param longitude
|
||||
* the longitude value.
|
||||
* @return the X coordinate on the current tile.
|
||||
*/
|
||||
|
||||
private static float scaleLongitude(float longitude) {
|
||||
return (float) ((longitude / 1000000.0 + 180) / 360 * mCurrentTileZoom) - mCurrentTileX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scale stroke factor for the given zoom level.
|
||||
*
|
||||
* @param zoomLevel
|
||||
* the zoom level for which the scale stroke factor should be set.
|
||||
*/
|
||||
private static void setScaleStrokeWidth(byte zoomLevel) {
|
||||
int zoomLevelDiff = Math.max(zoomLevel - STROKE_MIN_ZOOM_LEVEL, 0);
|
||||
DatabaseRenderer.renderTheme.scaleStrokeWidth((float) Math.pow(STROKE_INCREASE, zoomLevelDiff));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapRenderer getMapRenderer(MapView mapView) {
|
||||
return new MapRenderer(mapView);
|
||||
}
|
||||
}
|
||||
985
src/org/mapsforge/android/swrenderer/DependencyCache.java
Normal file
985
src/org/mapsforge/android/swrenderer/DependencyCache.java
Normal file
@@ -0,0 +1,985 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mapsforge.core.Tile;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* This class process the methods for the Dependency Cache. It's connected with the LabelPlacement class. The main goal
|
||||
* is, to remove double labels and symbols that are already rendered, from the actual tile. Labels and symbols that,
|
||||
* would be rendered on an already drawn Tile, will be deleted too.
|
||||
*/
|
||||
class DependencyCache {
|
||||
/**
|
||||
* The class holds the data for a symbol with dependencies on other tiles.
|
||||
*
|
||||
* @param <Type>
|
||||
* only two types are reasonable. The DependencySymbol or DependencyText class.
|
||||
*/
|
||||
private static class Dependency<Type> {
|
||||
ImmutablePoint point;
|
||||
final Type value;
|
||||
|
||||
Dependency(Type value, ImmutablePoint point) {
|
||||
this.value = value;
|
||||
this.point = point;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class holds all the information off the possible dependencies on a tile.
|
||||
*/
|
||||
private static class DependencyOnTile {
|
||||
boolean drawn;
|
||||
List<Dependency<DependencyText>> labels;
|
||||
List<Dependency<DependencySymbol>> symbols;
|
||||
|
||||
/**
|
||||
* Initialize label, symbol and drawn.
|
||||
*/
|
||||
DependencyOnTile() {
|
||||
this.labels = null;
|
||||
this.symbols = null;
|
||||
this.drawn = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toAdd
|
||||
* a dependency Symbol
|
||||
*/
|
||||
void addSymbol(Dependency<DependencySymbol> toAdd) {
|
||||
if (this.symbols == null) {
|
||||
this.symbols = new ArrayList<Dependency<DependencySymbol>>();
|
||||
}
|
||||
this.symbols.add(toAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toAdd
|
||||
* a Dependency Text
|
||||
*/
|
||||
void addText(Dependency<DependencyText> toAdd) {
|
||||
if (this.labels == null) {
|
||||
this.labels = new ArrayList<Dependency<DependencyText>>();
|
||||
}
|
||||
this.labels.add(toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The class holds the data for a symbol with dependencies on other tiles.
|
||||
*/
|
||||
private static class DependencySymbol {
|
||||
private final List<Tile> tiles;
|
||||
Bitmap symbol;
|
||||
|
||||
/**
|
||||
* Creates a symbol dependency element for the dependency cache.
|
||||
*
|
||||
* @param symbol
|
||||
* reference on the dependency symbol.
|
||||
* @param tile
|
||||
* dependency tile.
|
||||
*/
|
||||
DependencySymbol(Bitmap symbol, Tile tile) {
|
||||
this.symbol = symbol;
|
||||
this.tiles = new LinkedList<Tile>();
|
||||
this.tiles.add(tile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional tile, which has an dependency with this symbol.
|
||||
*
|
||||
* @param tile
|
||||
* additional tile.
|
||||
*/
|
||||
void addTile(Tile tile) {
|
||||
this.tiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The class holds the data for a label with dependencies on other tiles.
|
||||
*/
|
||||
private static class DependencyText {
|
||||
final Rect boundary;
|
||||
final Paint paintBack;
|
||||
final Paint paintFront;
|
||||
final String text;
|
||||
List<Tile> tiles;
|
||||
|
||||
/**
|
||||
* Creates a text dependency in the dependency cache.
|
||||
*
|
||||
* @param paintFront
|
||||
* paint element from the front.
|
||||
* @param paintBack
|
||||
* paint element form the background of the text.
|
||||
* @param text
|
||||
* the text of the element.
|
||||
* @param boundary
|
||||
* the fixed boundary with width and height.
|
||||
* @param tile
|
||||
* all tile in where the element has an influence.
|
||||
*/
|
||||
DependencyText(Paint paintFront, Paint paintBack, String text, Rect boundary, Tile tile) {
|
||||
this.paintFront = paintFront;
|
||||
this.paintBack = paintBack;
|
||||
this.text = text;
|
||||
this.tiles = new LinkedList<Tile>();
|
||||
this.tiles.add(tile);
|
||||
this.boundary = boundary;
|
||||
}
|
||||
|
||||
void addTile(Tile tile) {
|
||||
this.tiles.add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyOnTile currentDependencyOnTile;
|
||||
private Tile currentTile;
|
||||
|
||||
/**
|
||||
* Hash table, that connects the Tiles with their entries in the dependency cache.
|
||||
*/
|
||||
final Map<Tile, DependencyOnTile> dependencyTable;
|
||||
Dependency<DependencyText> depLabel;
|
||||
Rect rect1;
|
||||
Rect rect2;
|
||||
SymbolContainer smb;
|
||||
DependencyOnTile tmp;
|
||||
|
||||
/**
|
||||
* Constructor for this class, that creates a hashtable for the dependencies.
|
||||
*/
|
||||
DependencyCache() {
|
||||
this.dependencyTable = new Hashtable<Tile, DependencyOnTile>(60);
|
||||
}
|
||||
|
||||
private void addLabelsFromDependencyOnTile(List<PointTextContainer> labels) {
|
||||
for (int i = 0; i < this.currentDependencyOnTile.labels.size(); i++) {
|
||||
this.depLabel = this.currentDependencyOnTile.labels.get(i);
|
||||
if (this.depLabel.value.paintBack != null) {
|
||||
labels.add(new PointTextContainer(this.depLabel.value.text, this.depLabel.point.pointX,
|
||||
this.depLabel.point.pointY, this.depLabel.value.paintFront, this.depLabel.value.paintBack));
|
||||
} else {
|
||||
labels.add(new PointTextContainer(this.depLabel.value.text, this.depLabel.point.pointX,
|
||||
this.depLabel.point.pointY, this.depLabel.value.paintFront));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addSymbolsFromDependencyOnTile(List<SymbolContainer> symbols) {
|
||||
for (Dependency<DependencySymbol> depSmb : this.currentDependencyOnTile.symbols) {
|
||||
symbols.add(new SymbolContainer(depSmb.value.symbol, depSmb.point.pointX, depSmb.point.pointY));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the dependency entry from the tile and the neighbor tiles with the dependency information, that are
|
||||
* necessary for drawing. To do that every label and symbol that will be drawn, will be checked if it produces
|
||||
* dependencies with other tiles.
|
||||
*
|
||||
* @param pTC
|
||||
* list of the labels
|
||||
*/
|
||||
private void fillDependencyLabels(List<PointTextContainer> pTC) {
|
||||
Tile left = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile right = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile up = new Tile(this.currentTile.tileX, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile down = new Tile(this.currentTile.tileX, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
Tile leftup = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile leftdown = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
Tile rightup = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile rightdown = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
PointTextContainer label;
|
||||
DependencyOnTile linkedDep;
|
||||
DependencyText toAdd;
|
||||
|
||||
for (int i = 0; i < pTC.size(); i++) {
|
||||
|
||||
label = pTC.get(i);
|
||||
|
||||
toAdd = null;
|
||||
|
||||
// up
|
||||
if ((label.y - label.boundary.height() < 0.0f) && (!this.dependencyTable.get(up).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(up);
|
||||
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x,
|
||||
label.y)));
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x, label.y
|
||||
+ Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(up);
|
||||
|
||||
if ((label.x < 0.0f) && (!this.dependencyTable.get(leftup).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(leftup);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x + Tile.TILE_SIZE, label.y + Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(leftup);
|
||||
}
|
||||
|
||||
if ((label.x + label.boundary.width() > Tile.TILE_SIZE) && (!this.dependencyTable.get(rightup).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(rightup);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x - Tile.TILE_SIZE, label.y + Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(rightup);
|
||||
}
|
||||
}
|
||||
|
||||
// down
|
||||
if ((label.y > Tile.TILE_SIZE) && (!this.dependencyTable.get(down).drawn)) {
|
||||
|
||||
linkedDep = this.dependencyTable.get(down);
|
||||
|
||||
if (toAdd == null) {
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
|
||||
}
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x, label.y
|
||||
- Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(down);
|
||||
|
||||
if ((label.x < 0.0f) && (!this.dependencyTable.get(leftdown).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(leftdown);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x + Tile.TILE_SIZE, label.y - Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(leftdown);
|
||||
}
|
||||
|
||||
if ((label.x + label.boundary.width() > Tile.TILE_SIZE) && (!this.dependencyTable.get(rightdown).drawn)) {
|
||||
|
||||
linkedDep = this.dependencyTable.get(rightdown);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x - Tile.TILE_SIZE, label.y - Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(rightdown);
|
||||
}
|
||||
}
|
||||
// left
|
||||
|
||||
if ((label.x < 0.0f) && (!this.dependencyTable.get(left).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(left);
|
||||
|
||||
if (toAdd == null) {
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
}
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x + Tile.TILE_SIZE,
|
||||
label.y)));
|
||||
|
||||
toAdd.addTile(left);
|
||||
}
|
||||
// right
|
||||
if ((label.x + label.boundary.width() > Tile.TILE_SIZE) && (!this.dependencyTable.get(right).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(right);
|
||||
|
||||
if (toAdd == null) {
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
}
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x - Tile.TILE_SIZE,
|
||||
label.y)));
|
||||
|
||||
toAdd.addTile(right);
|
||||
}
|
||||
|
||||
// check symbols
|
||||
|
||||
if ((label.symbol != null) && (toAdd == null)) {
|
||||
|
||||
if ((label.symbol.y <= 0.0f) && (!this.dependencyTable.get(up).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(up);
|
||||
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x, label.y
|
||||
+ Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(up);
|
||||
|
||||
if ((label.symbol.x < 0.0f) && (!this.dependencyTable.get(leftup).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(leftup);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x
|
||||
+ Tile.TILE_SIZE, label.y + Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(leftup);
|
||||
}
|
||||
|
||||
if ((label.symbol.x + label.symbol.symbol.getWidth() > Tile.TILE_SIZE)
|
||||
&& (!this.dependencyTable.get(rightup).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(rightup);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x
|
||||
- Tile.TILE_SIZE, label.y + Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(rightup);
|
||||
}
|
||||
}
|
||||
|
||||
if ((label.symbol.y + label.symbol.symbol.getHeight() >= Tile.TILE_SIZE)
|
||||
&& (!this.dependencyTable.get(down).drawn)) {
|
||||
|
||||
linkedDep = this.dependencyTable.get(down);
|
||||
|
||||
if (toAdd == null) {
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
}
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x, label.y
|
||||
+ Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(up);
|
||||
|
||||
if ((label.symbol.x < 0.0f) && (!this.dependencyTable.get(leftdown).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(leftdown);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x
|
||||
+ Tile.TILE_SIZE, label.y - Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(leftdown);
|
||||
}
|
||||
|
||||
if ((label.symbol.x + label.symbol.symbol.getWidth() > Tile.TILE_SIZE)
|
||||
&& (!this.dependencyTable.get(rightdown).drawn)) {
|
||||
|
||||
linkedDep = this.dependencyTable.get(rightdown);
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(label.x
|
||||
- Tile.TILE_SIZE, label.y - Tile.TILE_SIZE)));
|
||||
|
||||
toAdd.addTile(rightdown);
|
||||
}
|
||||
}
|
||||
|
||||
if ((label.symbol.x <= 0.0f) && (!this.dependencyTable.get(left).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(left);
|
||||
|
||||
if (toAdd == null) {
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
}
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x - Tile.TILE_SIZE, label.y)));
|
||||
|
||||
toAdd.addTile(left);
|
||||
}
|
||||
|
||||
if ((label.symbol.x + label.symbol.symbol.getWidth() >= Tile.TILE_SIZE)
|
||||
&& (!this.dependencyTable.get(right).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(right);
|
||||
|
||||
if (toAdd == null) {
|
||||
toAdd = new DependencyText(label.paintFront, label.paintBack, label.text, label.boundary,
|
||||
this.currentTile);
|
||||
|
||||
this.currentDependencyOnTile.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x, label.y)));
|
||||
}
|
||||
|
||||
linkedDep.addText(new Dependency<DependencyText>(toAdd, new ImmutablePoint(
|
||||
label.x + Tile.TILE_SIZE, label.y)));
|
||||
|
||||
toAdd.addTile(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fillDependencyOnTile2(List<PointTextContainer> labels, List<SymbolContainer> symbols,
|
||||
List<PointTextContainer> areaLabels) {
|
||||
Tile left = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile right = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile up = new Tile(this.currentTile.tileX, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile down = new Tile(this.currentTile.tileX, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
Tile leftup = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile leftdown = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
Tile rightup = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile rightdown = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
if (this.dependencyTable.get(up) == null) {
|
||||
this.dependencyTable.put(up, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(down) == null) {
|
||||
this.dependencyTable.put(down, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(left) == null) {
|
||||
this.dependencyTable.put(left, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(right) == null) {
|
||||
this.dependencyTable.put(right, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(leftdown) == null) {
|
||||
this.dependencyTable.put(leftdown, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(rightup) == null) {
|
||||
this.dependencyTable.put(rightup, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(leftup) == null) {
|
||||
this.dependencyTable.put(leftup, new DependencyOnTile());
|
||||
}
|
||||
if (this.dependencyTable.get(rightdown) == null) {
|
||||
this.dependencyTable.put(rightdown, new DependencyOnTile());
|
||||
}
|
||||
|
||||
fillDependencyLabels(labels);
|
||||
fillDependencyLabels(areaLabels);
|
||||
|
||||
DependencyOnTile linkedDep;
|
||||
DependencySymbol addSmb;
|
||||
|
||||
for (SymbolContainer symbol : symbols) {
|
||||
addSmb = null;
|
||||
|
||||
// up
|
||||
if ((symbol.y < 0.0f) && (!this.dependencyTable.get(up).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(up);
|
||||
|
||||
addSmb = new DependencySymbol(symbol.symbol, this.currentTile);
|
||||
this.currentDependencyOnTile.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(
|
||||
symbol.x, symbol.y)));
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x, symbol.y
|
||||
+ Tile.TILE_SIZE)));
|
||||
addSmb.addTile(up);
|
||||
|
||||
if ((symbol.x < 0.0f) && (!this.dependencyTable.get(leftup).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(leftup);
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x
|
||||
+ Tile.TILE_SIZE, symbol.y + Tile.TILE_SIZE)));
|
||||
addSmb.addTile(leftup);
|
||||
}
|
||||
|
||||
if ((symbol.x + symbol.symbol.getWidth() > Tile.TILE_SIZE)
|
||||
&& (!this.dependencyTable.get(rightup).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(rightup);
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x
|
||||
- Tile.TILE_SIZE, symbol.y + Tile.TILE_SIZE)));
|
||||
addSmb.addTile(rightup);
|
||||
}
|
||||
}
|
||||
|
||||
// down
|
||||
if ((symbol.y + symbol.symbol.getHeight() > Tile.TILE_SIZE) && (!this.dependencyTable.get(down).drawn)) {
|
||||
|
||||
linkedDep = this.dependencyTable.get(down);
|
||||
|
||||
if (addSmb == null) {
|
||||
addSmb = new DependencySymbol(symbol.symbol, this.currentTile);
|
||||
this.currentDependencyOnTile.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(
|
||||
symbol.x, symbol.y)));
|
||||
}
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x, symbol.y
|
||||
- Tile.TILE_SIZE)));
|
||||
addSmb.addTile(down);
|
||||
|
||||
if ((symbol.x < 0.0f) && (!this.dependencyTable.get(leftdown).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(leftdown);
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x
|
||||
+ Tile.TILE_SIZE, symbol.y - Tile.TILE_SIZE)));
|
||||
addSmb.addTile(leftdown);
|
||||
}
|
||||
|
||||
if ((symbol.x + symbol.symbol.getWidth() > Tile.TILE_SIZE)
|
||||
&& (!this.dependencyTable.get(rightdown).drawn)) {
|
||||
|
||||
linkedDep = this.dependencyTable.get(rightdown);
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x
|
||||
- Tile.TILE_SIZE, symbol.y - Tile.TILE_SIZE)));
|
||||
addSmb.addTile(rightdown);
|
||||
}
|
||||
}
|
||||
|
||||
// left
|
||||
if ((symbol.x < 0.0f) && (!this.dependencyTable.get(left).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(left);
|
||||
|
||||
if (addSmb == null) {
|
||||
addSmb = new DependencySymbol(symbol.symbol, this.currentTile);
|
||||
this.currentDependencyOnTile.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(
|
||||
symbol.x, symbol.y)));
|
||||
}
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x
|
||||
+ Tile.TILE_SIZE, symbol.y)));
|
||||
addSmb.addTile(left);
|
||||
}
|
||||
|
||||
// right
|
||||
if ((symbol.x + symbol.symbol.getWidth() > Tile.TILE_SIZE) && (!this.dependencyTable.get(right).drawn)) {
|
||||
linkedDep = this.dependencyTable.get(right);
|
||||
if (addSmb == null) {
|
||||
addSmb = new DependencySymbol(symbol.symbol, this.currentTile);
|
||||
this.currentDependencyOnTile.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(
|
||||
symbol.x, symbol.y)));
|
||||
}
|
||||
|
||||
linkedDep.addSymbol(new Dependency<DependencySymbol>(addSmb, new ImmutablePoint(symbol.x
|
||||
- Tile.TILE_SIZE, symbol.y)));
|
||||
addSmb.addTile(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOverlappingAreaLabelsWithDependencyLabels(List<PointTextContainer> areaLabels) {
|
||||
PointTextContainer pTC;
|
||||
|
||||
for (int i = 0; i < this.currentDependencyOnTile.labels.size(); i++) {
|
||||
this.depLabel = this.currentDependencyOnTile.labels.get(i);
|
||||
this.rect1 = new android.graphics.Rect((int) (this.depLabel.point.pointX),
|
||||
(int) (this.depLabel.point.pointY - this.depLabel.value.boundary.height()),
|
||||
(int) (this.depLabel.point.pointX + this.depLabel.value.boundary.width()),
|
||||
(int) (this.depLabel.point.pointY));
|
||||
|
||||
for (int x = 0; x < areaLabels.size(); x++) {
|
||||
pTC = areaLabels.get(x);
|
||||
|
||||
this.rect2 = new android.graphics.Rect((int) pTC.x, (int) pTC.y - pTC.boundary.height(), (int) pTC.x
|
||||
+ pTC.boundary.width(), (int) pTC.y);
|
||||
|
||||
if (android.graphics.Rect.intersects(this.rect2, this.rect1)) {
|
||||
areaLabels.remove(x);
|
||||
x--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOverlappingAreaLabelsWithDependencySymbols(List<PointTextContainer> areaLabels) {
|
||||
PointTextContainer label;
|
||||
|
||||
for (Dependency<DependencySymbol> depSmb : this.currentDependencyOnTile.symbols) {
|
||||
|
||||
this.rect1 = new android.graphics.Rect((int) depSmb.point.pointX, (int) depSmb.point.pointY,
|
||||
(int) depSmb.point.pointX + depSmb.value.symbol.getWidth(), (int) depSmb.point.pointY
|
||||
+ depSmb.value.symbol.getHeight());
|
||||
|
||||
for (int x = 0; x < areaLabels.size(); x++) {
|
||||
label = areaLabels.get(x);
|
||||
|
||||
this.rect2 = new android.graphics.Rect((int) (label.x), (int) (label.y - label.boundary.height()),
|
||||
(int) (label.x + label.boundary.width()), (int) (label.y));
|
||||
|
||||
if (android.graphics.Rect.intersects(this.rect2, this.rect1)) {
|
||||
areaLabels.remove(x);
|
||||
x--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOverlappingLabelsWithDependencyLabels(List<PointTextContainer> labels) {
|
||||
for (int i = 0; i < this.currentDependencyOnTile.labels.size(); i++) {
|
||||
for (int x = 0; x < labels.size(); x++) {
|
||||
if ((labels.get(x).text.equals(this.currentDependencyOnTile.labels.get(i).value.text))
|
||||
&& (labels.get(x).paintFront
|
||||
.equals(this.currentDependencyOnTile.labels.get(i).value.paintFront))
|
||||
&& (labels.get(x).paintBack.equals(this.currentDependencyOnTile.labels.get(i).value.paintBack))) {
|
||||
labels.remove(x);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOverlappingSymbolsWithDepencySymbols(List<SymbolContainer> symbols, int dis) {
|
||||
SymbolContainer sym;
|
||||
Dependency<DependencySymbol> sym2;
|
||||
|
||||
for (int x = 0; x < this.currentDependencyOnTile.symbols.size(); x++) {
|
||||
sym2 = this.currentDependencyOnTile.symbols.get(x);
|
||||
this.rect1 = new android.graphics.Rect((int) sym2.point.pointX - dis, (int) sym2.point.pointY - dis,
|
||||
(int) sym2.point.pointX + sym2.value.symbol.getWidth() + dis, (int) sym2.point.pointY
|
||||
+ sym2.value.symbol.getHeight() + dis);
|
||||
|
||||
for (int y = 0; y < symbols.size(); y++) {
|
||||
|
||||
sym = symbols.get(y);
|
||||
this.rect2 = new android.graphics.Rect((int) sym.x, (int) sym.y, (int) sym.x + sym.symbol.getWidth(),
|
||||
(int) sym.y + sym.symbol.getHeight());
|
||||
|
||||
if (android.graphics.Rect.intersects(this.rect2, this.rect1)) {
|
||||
symbols.remove(y);
|
||||
y--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOverlappingSymbolsWithDependencyLabels(List<SymbolContainer> symbols) {
|
||||
for (int i = 0; i < this.currentDependencyOnTile.labels.size(); i++) {
|
||||
this.depLabel = this.currentDependencyOnTile.labels.get(i);
|
||||
this.rect1 = new android.graphics.Rect((int) (this.depLabel.point.pointX),
|
||||
(int) (this.depLabel.point.pointY - this.depLabel.value.boundary.height()),
|
||||
(int) (this.depLabel.point.pointX + this.depLabel.value.boundary.width()),
|
||||
(int) (this.depLabel.point.pointY));
|
||||
|
||||
for (int x = 0; x < symbols.size(); x++) {
|
||||
this.smb = symbols.get(x);
|
||||
|
||||
this.rect2 = new android.graphics.Rect((int) this.smb.x, (int) this.smb.y, (int) this.smb.x
|
||||
+ this.smb.symbol.getWidth(), (int) this.smb.y + this.smb.symbol.getHeight());
|
||||
|
||||
if (android.graphics.Rect.intersects(this.rect2, this.rect1)) {
|
||||
symbols.remove(x);
|
||||
x--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fills the entries in the dependency cache of the tiles, if their dependencies.
|
||||
*
|
||||
* @param labels
|
||||
* current labels, that will be displayed.
|
||||
* @param symbols
|
||||
* current symbols, that will be displayed.
|
||||
* @param areaLabels
|
||||
* current areaLabels, that will be displayed.
|
||||
*/
|
||||
void fillDependencyOnTile(List<PointTextContainer> labels, List<SymbolContainer> symbols,
|
||||
List<PointTextContainer> areaLabels) {
|
||||
this.currentDependencyOnTile.drawn = true;
|
||||
|
||||
if ((!labels.isEmpty()) || (!symbols.isEmpty()) || (!areaLabels.isEmpty())) {
|
||||
fillDependencyOnTile2(labels, symbols, areaLabels);
|
||||
}
|
||||
|
||||
if (this.currentDependencyOnTile.labels != null) {
|
||||
addLabelsFromDependencyOnTile(labels);
|
||||
}
|
||||
if (this.currentDependencyOnTile.symbols != null) {
|
||||
addSymbolsFromDependencyOnTile(symbols);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be called, before the dependencies will be handled correctly. Because it sets the actual Tile
|
||||
* and looks if it has already dependencies.
|
||||
*
|
||||
* @param tile
|
||||
* the current Tile
|
||||
*/
|
||||
void generateTileAndDependencyOnTile(Tile tile) {
|
||||
this.currentTile = new Tile(tile.tileX, tile.tileY, tile.zoomLevel);
|
||||
this.currentDependencyOnTile = this.dependencyTable.get(this.currentTile);
|
||||
|
||||
if (this.currentDependencyOnTile == null) {
|
||||
this.dependencyTable.put(this.currentTile, new DependencyOnTile());
|
||||
this.currentDependencyOnTile = this.dependencyTable.get(this.currentTile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the are labels from the actual list, that would be rendered in a Tile that has already be drawn.
|
||||
*
|
||||
* @param areaLabels
|
||||
* current area Labels, that will be displayed
|
||||
*/
|
||||
void removeAreaLabelsInAlreadyDrawnAreas(List<PointTextContainer> areaLabels) {
|
||||
Tile lefttmp = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile righttmp = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile uptmp = new Tile(this.currentTile.tileX, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile downtmp = new Tile(this.currentTile.tileX, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
boolean up;
|
||||
boolean left;
|
||||
boolean right;
|
||||
boolean down;
|
||||
|
||||
this.tmp = this.dependencyTable.get(lefttmp);
|
||||
left = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(righttmp);
|
||||
right = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(uptmp);
|
||||
up = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(downtmp);
|
||||
down = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
PointTextContainer label;
|
||||
|
||||
for (int i = 0; i < areaLabels.size(); i++) {
|
||||
label = areaLabels.get(i);
|
||||
|
||||
if (up && label.y - label.boundary.height() < 0.0f) {
|
||||
areaLabels.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (down && label.y > Tile.TILE_SIZE) {
|
||||
areaLabels.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (left && label.x < 0.0f) {
|
||||
areaLabels.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (right && label.x + label.boundary.width() > Tile.TILE_SIZE) {
|
||||
areaLabels.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all objects that overlaps with the objects from the dependency cache.
|
||||
*
|
||||
* @param labels
|
||||
* labels from the current tile
|
||||
* @param areaLabels
|
||||
* area labels from the current tile
|
||||
* @param symbols
|
||||
* symbols from the current tile
|
||||
*/
|
||||
void removeOverlappingObjectsWithDependencyOnTile(List<PointTextContainer> labels,
|
||||
List<PointTextContainer> areaLabels, List<SymbolContainer> symbols) {
|
||||
if (this.currentDependencyOnTile.labels != null && this.currentDependencyOnTile.labels.size() != 0) {
|
||||
removeOverlappingLabelsWithDependencyLabels(labels);
|
||||
removeOverlappingSymbolsWithDependencyLabels(symbols);
|
||||
removeOverlappingAreaLabelsWithDependencyLabels(areaLabels);
|
||||
}
|
||||
|
||||
if (this.currentDependencyOnTile.symbols != null && this.currentDependencyOnTile.symbols.size() != 0) {
|
||||
removeOverlappingSymbolsWithDepencySymbols(symbols, 2);
|
||||
removeOverlappingAreaLabelsWithDependencySymbols(areaLabels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the LabelPlacement class generates potential label positions for an POI, there should be no possible
|
||||
* positions, that collide with existing symbols or labels in the dependency Cache. This class implements this
|
||||
* functionality.
|
||||
*
|
||||
* @param refPos
|
||||
* possible label positions form the two or four point Greedy
|
||||
*/
|
||||
void removeReferencePointsFromDependencyCache(LabelPlacement.ReferencePosition[] refPos) {
|
||||
Tile lefttmp = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile righttmp = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile uptmp = new Tile(this.currentTile.tileX, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile downtmp = new Tile(this.currentTile.tileX, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
boolean up;
|
||||
boolean left;
|
||||
boolean right;
|
||||
boolean down;
|
||||
|
||||
this.tmp = this.dependencyTable.get(lefttmp);
|
||||
left = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(righttmp);
|
||||
right = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(uptmp);
|
||||
up = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(downtmp);
|
||||
down = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
LabelPlacement.ReferencePosition ref;
|
||||
|
||||
for (int i = 0; i < refPos.length; i++) {
|
||||
ref = refPos[i];
|
||||
|
||||
if (ref == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (up && ref.y - ref.height < 0) {
|
||||
refPos[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (down && ref.y >= Tile.TILE_SIZE) {
|
||||
refPos[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (left && ref.x < 0) {
|
||||
refPos[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (right && ref.x + ref.width > Tile.TILE_SIZE) {
|
||||
refPos[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// removes all Reverence Points that intersects with Labels from the Dependency Cache
|
||||
|
||||
int dis = 2;
|
||||
if (this.currentDependencyOnTile != null) {
|
||||
if (this.currentDependencyOnTile.labels != null) {
|
||||
for (int i = 0; i < this.currentDependencyOnTile.labels.size(); i++) {
|
||||
this.depLabel = this.currentDependencyOnTile.labels.get(i);
|
||||
this.rect1 = new android.graphics.Rect((int) this.depLabel.point.pointX - dis,
|
||||
(int) (this.depLabel.point.pointY - this.depLabel.value.boundary.height()) - dis,
|
||||
(int) (this.depLabel.point.pointX + this.depLabel.value.boundary.width() + dis),
|
||||
(int) (this.depLabel.point.pointY + dis));
|
||||
|
||||
for (int y = 0; y < refPos.length; y++) {
|
||||
if (refPos[y] != null) {
|
||||
this.rect2 = new android.graphics.Rect((int) refPos[y].x,
|
||||
(int) (refPos[y].y - refPos[y].height), (int) (refPos[y].x + refPos[y].width),
|
||||
(int) (refPos[y].y));
|
||||
|
||||
if (android.graphics.Rect.intersects(this.rect2, this.rect1)) {
|
||||
refPos[y] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.currentDependencyOnTile.symbols != null) {
|
||||
for (Dependency<DependencySymbol> symbols2 : this.currentDependencyOnTile.symbols) {
|
||||
|
||||
this.rect1 = new android.graphics.Rect((int) symbols2.point.pointX, (int) (symbols2.point.pointY),
|
||||
(int) (symbols2.point.pointX + symbols2.value.symbol.getWidth()),
|
||||
(int) (symbols2.point.pointY + symbols2.value.symbol.getHeight()));
|
||||
|
||||
for (int y = 0; y < refPos.length; y++) {
|
||||
if (refPos[y] != null) {
|
||||
this.rect2 = new android.graphics.Rect((int) refPos[y].x,
|
||||
(int) (refPos[y].y - refPos[y].height), (int) (refPos[y].x + refPos[y].width),
|
||||
(int) (refPos[y].y));
|
||||
|
||||
if (android.graphics.Rect.intersects(this.rect2, this.rect1)) {
|
||||
refPos[y] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeSymbolsFromDrawnAreas(List<SymbolContainer> symbols) {
|
||||
Tile lefttmp = new Tile(this.currentTile.tileX - 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile righttmp = new Tile(this.currentTile.tileX + 1, this.currentTile.tileY, this.currentTile.zoomLevel);
|
||||
Tile uptmp = new Tile(this.currentTile.tileX, this.currentTile.tileY - 1, this.currentTile.zoomLevel);
|
||||
Tile downtmp = new Tile(this.currentTile.tileX, this.currentTile.tileY + 1, this.currentTile.zoomLevel);
|
||||
|
||||
boolean up;
|
||||
boolean left;
|
||||
boolean right;
|
||||
boolean down;
|
||||
|
||||
this.tmp = this.dependencyTable.get(lefttmp);
|
||||
left = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(righttmp);
|
||||
right = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(uptmp);
|
||||
up = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
this.tmp = this.dependencyTable.get(downtmp);
|
||||
down = this.tmp == null ? false : this.tmp.drawn;
|
||||
|
||||
SymbolContainer ref;
|
||||
|
||||
for (int i = 0; i < symbols.size(); i++) {
|
||||
ref = symbols.get(i);
|
||||
|
||||
if (up && ref.y < 0) {
|
||||
symbols.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (down && ref.y + ref.symbol.getHeight() > Tile.TILE_SIZE) {
|
||||
symbols.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (left && ref.x < 0) {
|
||||
symbols.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (right && ref.x + ref.symbol.getWidth() > Tile.TILE_SIZE) {
|
||||
symbols.remove(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/org/mapsforge/android/swrenderer/GLMapTile.java
Normal file
83
src/org/mapsforge/android/swrenderer/GLMapTile.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.mapsforge.android.swrenderer;
|
||||
|
||||
import org.mapsforge.android.mapgenerator.MapTile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class GLMapTile extends MapTile {
|
||||
private float mScale;
|
||||
|
||||
final GLMapTile[] child = { null, null, null, null };
|
||||
GLMapTile parent;
|
||||
|
||||
// private long mLoadTime;
|
||||
private int mTextureID;
|
||||
|
||||
/**
|
||||
* @param tileX
|
||||
* ...
|
||||
* @param tileY
|
||||
* ...
|
||||
* @param zoomLevel
|
||||
* ..
|
||||
*/
|
||||
public GLMapTile(long tileX, long tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
mScale = 1;
|
||||
isDrawn = false;
|
||||
mTextureID = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ...
|
||||
*/
|
||||
public int getTexture() {
|
||||
return mTextureID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mTextureID
|
||||
* ...
|
||||
*/
|
||||
public void setTexture(int mTextureID) {
|
||||
this.mTextureID = mTextureID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ...
|
||||
*/
|
||||
public boolean hasTexture() {
|
||||
return mTextureID >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ...
|
||||
*/
|
||||
public float getScale() {
|
||||
return mScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale
|
||||
* ...
|
||||
*/
|
||||
public void setScale(float scale) {
|
||||
mScale = scale;
|
||||
}
|
||||
|
||||
}
|
||||
116
src/org/mapsforge/android/swrenderer/ImmutablePoint.java
Normal file
116
src/org/mapsforge/android/swrenderer/ImmutablePoint.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
/**
|
||||
* An ImmutablePoint represents an fixed pair of float coordinates.
|
||||
*/
|
||||
class ImmutablePoint implements Comparable<ImmutablePoint> {
|
||||
/**
|
||||
* Subtracts the x and y coordinates of one point from another point.
|
||||
*
|
||||
* @param minuend
|
||||
* the minuend.
|
||||
* @param subtrahend
|
||||
* the subtrahend.
|
||||
* @return a new Point object.
|
||||
*/
|
||||
static ImmutablePoint substract(ImmutablePoint minuend, ImmutablePoint subtrahend) {
|
||||
return new ImmutablePoint(minuend.pointX - subtrahend.pointX, minuend.pointY - subtrahend.pointY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the hash code of this object.
|
||||
*/
|
||||
private final int hashCodeValue;
|
||||
|
||||
/**
|
||||
* X coordinate of this point.
|
||||
*/
|
||||
final float pointX;
|
||||
|
||||
/**
|
||||
* Y coordinate of this point.
|
||||
*/
|
||||
final float pointY;
|
||||
|
||||
/**
|
||||
* @param x
|
||||
* the x coordinate of the point.
|
||||
* @param y
|
||||
* the y coordinate of the point.
|
||||
*/
|
||||
ImmutablePoint(float x, float y) {
|
||||
this.pointX = x;
|
||||
this.pointY = y;
|
||||
this.hashCodeValue = calculateHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ImmutablePoint point) {
|
||||
if (this.pointX > point.pointX) {
|
||||
return 1;
|
||||
} else if (this.pointX < point.pointX) {
|
||||
return -1;
|
||||
} else if (this.pointY > point.pointY) {
|
||||
return 1;
|
||||
} else if (this.pointY < point.pointY) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
} else if (!(obj instanceof ImmutablePoint)) {
|
||||
return false;
|
||||
}
|
||||
ImmutablePoint other = (ImmutablePoint) obj;
|
||||
if (this.pointX != other.pointX) {
|
||||
return false;
|
||||
} else if (this.pointY != other.pointY) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.hashCodeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("ImmutablePoint [x=");
|
||||
stringBuilder.append(this.pointX);
|
||||
stringBuilder.append(", y=");
|
||||
stringBuilder.append(this.pointY);
|
||||
stringBuilder.append("]");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the hash code of this object.
|
||||
*/
|
||||
private int calculateHashCode() {
|
||||
int result = 7;
|
||||
result = 31 * result + Float.floatToIntBits(this.pointX);
|
||||
result = 31 * result + Float.floatToIntBits(this.pointY);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
766
src/org/mapsforge/android/swrenderer/LabelPlacement.java
Normal file
766
src/org/mapsforge/android/swrenderer/LabelPlacement.java
Normal file
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import org.mapsforge.core.Tile;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* This class place the labels form POIs, area labels and normal labels. The main target is avoiding collisions of these
|
||||
* different labels.
|
||||
*/
|
||||
class LabelPlacement {
|
||||
/**
|
||||
* This class holds the reference positions for the two and four point greedy algorithms.
|
||||
*/
|
||||
static class ReferencePosition {
|
||||
final float height;
|
||||
final int nodeNumber;
|
||||
SymbolContainer symbol;
|
||||
final float width;
|
||||
final float x;
|
||||
final float y;
|
||||
|
||||
ReferencePosition(float x, float y, int nodeNumber, float width, float height, SymbolContainer symbol) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.nodeNumber = nodeNumber;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.symbol = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ReferencePositionHeightComparator implements Comparator<ReferencePosition>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
static final ReferencePositionHeightComparator INSTANCE = new ReferencePositionHeightComparator();
|
||||
|
||||
private ReferencePositionHeightComparator() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ReferencePosition x, ReferencePosition y) {
|
||||
if (x.y - x.height < y.y - y.height) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (x.y - x.height > y.y - y.height) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ReferencePositionWidthComparator implements Comparator<ReferencePosition>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
static final ReferencePositionWidthComparator INSTANCE = new ReferencePositionWidthComparator();
|
||||
|
||||
private ReferencePositionWidthComparator() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ReferencePosition x, ReferencePosition y) {
|
||||
if (x.x + x.width < y.x + y.width) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (x.x + x.width > y.x + y.width) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ReferencePositionXComparator implements Comparator<ReferencePosition>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
static final ReferencePositionXComparator INSTANCE = new ReferencePositionXComparator();
|
||||
|
||||
private ReferencePositionXComparator() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ReferencePosition x, ReferencePosition y) {
|
||||
if (x.x < y.x) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (x.x > y.x) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ReferencePositionYComparator implements Comparator<ReferencePosition>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
static final ReferencePositionYComparator INSTANCE = new ReferencePositionYComparator();
|
||||
|
||||
private ReferencePositionYComparator() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ReferencePosition x, ReferencePosition y) {
|
||||
if (x.y < y.y) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (x.y > y.y) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int PLACEMENT_MODEL = 1;
|
||||
private int mLabelDistanceToLabel = 2;
|
||||
private int mLabelDistanceToSymbol = 2;
|
||||
// You can choose between 2 Position and 4 Position
|
||||
// placement Model 0 - 2-Position 1 - 4 Position
|
||||
// distance adjustments
|
||||
private int mStartDistanceToSymbols = 4;
|
||||
private int mSymbolDistanceToSymbol = 2;
|
||||
|
||||
final DependencyCache mDependencyCache;
|
||||
PointTextContainer mLabel;
|
||||
Rect mRect1;
|
||||
Rect mRect2;
|
||||
ReferencePosition mReferencePosition;
|
||||
SymbolContainer mSymbolContainer;
|
||||
|
||||
LabelPlacement() {
|
||||
mDependencyCache = new DependencyCache();
|
||||
mRect1 = new Rect();
|
||||
mRect2 = new Rect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers the labels.
|
||||
*
|
||||
* @param labels
|
||||
* labels to center
|
||||
*/
|
||||
private void centerLabels(List<PointTextContainer> labels) {
|
||||
for (int i = 0; i < labels.size(); i++) {
|
||||
mLabel = labels.get(i);
|
||||
mLabel.x = mLabel.x - mLabel.boundary.width() / 2;
|
||||
}
|
||||
}
|
||||
|
||||
private void preprocessAreaLabels(List<PointTextContainer> areaLabels) {
|
||||
centerLabels(areaLabels);
|
||||
|
||||
removeOutOfTileAreaLabels(areaLabels);
|
||||
|
||||
removeOverlappingAreaLabels(areaLabels);
|
||||
|
||||
if (!areaLabels.isEmpty()) {
|
||||
mDependencyCache.removeAreaLabelsInAlreadyDrawnAreas(areaLabels);
|
||||
}
|
||||
}
|
||||
|
||||
private void preprocessLabels(List<PointTextContainer> labels) {
|
||||
removeOutOfTileLabels(labels);
|
||||
}
|
||||
|
||||
private void preprocessSymbols(List<SymbolContainer> symbols) {
|
||||
removeOutOfTileSymbols(symbols);
|
||||
removeOverlappingSymbols(symbols);
|
||||
mDependencyCache.removeSymbolsFromDrawnAreas(symbols);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method uses an adapted greedy strategy for the fixed four position model, above, under left and right form
|
||||
* the point of interest. It uses no priority search tree, because it will not function with symbols only with
|
||||
* points. Instead it uses two minimum heaps. They work similar to a sweep line algorithm but have not a O(n log n
|
||||
* +k) runtime. To find the rectangle that has the top edge, I use also a minimum Heap. The rectangles are sorted by
|
||||
* their y coordinates.
|
||||
*
|
||||
* @param labels
|
||||
* label positions and text
|
||||
* @param symbols
|
||||
* symbol positions
|
||||
* @param areaLabels
|
||||
* area label positions and text
|
||||
* @return list of labels without overlaps with symbols and other labels by the four fixed position greedy strategy
|
||||
*/
|
||||
private List<PointTextContainer> processFourPointGreedy(List<PointTextContainer> labels,
|
||||
List<SymbolContainer> symbols, List<PointTextContainer> areaLabels) {
|
||||
List<PointTextContainer> resolutionSet = new ArrayList<PointTextContainer>();
|
||||
|
||||
// Array for the generated reference positions around the points of
|
||||
// interests
|
||||
ReferencePosition[] refPos = new ReferencePosition[(labels.size()) * 4];
|
||||
|
||||
// lists that sorts the reference points after the minimum top edge y
|
||||
// position
|
||||
PriorityQueue<ReferencePosition> priorUp = new PriorityQueue<ReferencePosition>(labels.size() * 4 * 2
|
||||
+ labels.size() / 10 * 2, ReferencePositionYComparator.INSTANCE);
|
||||
// lists that sorts the reference points after the minimum bottom edge y
|
||||
// position
|
||||
PriorityQueue<ReferencePosition> priorDown = new PriorityQueue<ReferencePosition>(labels.size() * 4 * 2
|
||||
+ labels.size() / 10 * 2, ReferencePositionHeightComparator.INSTANCE);
|
||||
|
||||
PointTextContainer tmp;
|
||||
int dis = mStartDistanceToSymbols;
|
||||
|
||||
// creates the reference positions
|
||||
for (int z = 0; z < labels.size(); z++) {
|
||||
if (labels.get(z) != null) {
|
||||
if (labels.get(z).symbol != null) {
|
||||
tmp = labels.get(z);
|
||||
|
||||
// up
|
||||
refPos[z * 4] = new ReferencePosition(tmp.x - tmp.boundary.width() / 2, tmp.y
|
||||
- tmp.symbol.symbol.getHeight() / 2 - dis, z, tmp.boundary.width(), tmp.boundary.height(),
|
||||
tmp.symbol);
|
||||
// down
|
||||
refPos[z * 4 + 1] = new ReferencePosition(tmp.x - tmp.boundary.width() / 2, tmp.y
|
||||
+ tmp.symbol.symbol.getHeight() / 2 + tmp.boundary.height() + dis, z, tmp.boundary.width(),
|
||||
tmp.boundary.height(), tmp.symbol);
|
||||
// left
|
||||
refPos[z * 4 + 2] = new ReferencePosition(tmp.x - tmp.symbol.symbol.getWidth() / 2
|
||||
- tmp.boundary.width() - dis, tmp.y + tmp.boundary.height() / 2, z, tmp.boundary.width(),
|
||||
tmp.boundary.height(), tmp.symbol);
|
||||
// right
|
||||
refPos[z * 4 + 3] = new ReferencePosition(tmp.x + tmp.symbol.symbol.getWidth() / 2 + dis, tmp.y
|
||||
+ tmp.boundary.height() / 2 - 0.1f, z, tmp.boundary.width(), tmp.boundary.height(),
|
||||
tmp.symbol);
|
||||
} else {
|
||||
refPos[z * 4] = new ReferencePosition(labels.get(z).x - ((labels.get(z).boundary.width()) / 2),
|
||||
labels.get(z).y, z, labels.get(z).boundary.width(), labels.get(z).boundary.height(), null);
|
||||
refPos[z * 4 + 1] = null;
|
||||
refPos[z * 4 + 2] = null;
|
||||
refPos[z * 4 + 3] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeNonValidateReferencePosition(refPos, symbols, areaLabels);
|
||||
|
||||
// do while it gives reference positions
|
||||
for (int i = 0; i < refPos.length; i++) {
|
||||
mReferencePosition = refPos[i];
|
||||
if (mReferencePosition != null) {
|
||||
priorUp.add(mReferencePosition);
|
||||
priorDown.add(mReferencePosition);
|
||||
}
|
||||
}
|
||||
|
||||
while (priorUp.size() != 0) {
|
||||
mReferencePosition = priorUp.remove();
|
||||
|
||||
mLabel = labels.get(mReferencePosition.nodeNumber);
|
||||
|
||||
resolutionSet.add(new PointTextContainer(mLabel.text, mReferencePosition.x,
|
||||
mReferencePosition.y, mLabel.paintFront, mLabel.paintBack, mLabel.symbol));
|
||||
|
||||
if (priorUp.size() == 0) {
|
||||
return resolutionSet;
|
||||
}
|
||||
|
||||
priorUp.remove(refPos[mReferencePosition.nodeNumber * 4 + 0]);
|
||||
priorUp.remove(refPos[mReferencePosition.nodeNumber * 4 + 1]);
|
||||
priorUp.remove(refPos[mReferencePosition.nodeNumber * 4 + 2]);
|
||||
priorUp.remove(refPos[mReferencePosition.nodeNumber * 4 + 3]);
|
||||
|
||||
priorDown.remove(refPos[mReferencePosition.nodeNumber * 4 + 0]);
|
||||
priorDown.remove(refPos[mReferencePosition.nodeNumber * 4 + 1]);
|
||||
priorDown.remove(refPos[mReferencePosition.nodeNumber * 4 + 2]);
|
||||
priorDown.remove(refPos[mReferencePosition.nodeNumber * 4 + 3]);
|
||||
|
||||
LinkedList<ReferencePosition> linkedRef = new LinkedList<ReferencePosition>();
|
||||
|
||||
while (priorDown.size() != 0) {
|
||||
if (priorDown.peek().x < mReferencePosition.x + mReferencePosition.width) {
|
||||
linkedRef.add(priorDown.remove());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// brute Force collision test (faster then sweep line for a small
|
||||
// amount of
|
||||
// objects)
|
||||
for (int i = 0; i < linkedRef.size(); i++) {
|
||||
if ((linkedRef.get(i).x <= mReferencePosition.x + mReferencePosition.width)
|
||||
&& (linkedRef.get(i).y >= mReferencePosition.y - linkedRef.get(i).height)
|
||||
&& (linkedRef.get(i).y <= mReferencePosition.y + linkedRef.get(i).height)) {
|
||||
priorUp.remove(linkedRef.get(i));
|
||||
linkedRef.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
priorDown.addAll(linkedRef);
|
||||
}
|
||||
|
||||
return resolutionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method uses an adapted greedy strategy for the fixed two position model, above and under. It uses no
|
||||
* priority search tree, because it will not function with symbols only with points. Instead it uses two minimum
|
||||
* heaps. They work similar to a sweep line algorithm but have not a O(n log n +k) runtime. To find the rectangle
|
||||
* that has the leftest edge, I use also a minimum Heap. The rectangles are sorted by their x coordinates.
|
||||
*
|
||||
* @param labels
|
||||
* label positions and text
|
||||
* @param symbols
|
||||
* symbol positions
|
||||
* @param areaLabels
|
||||
* area label positions and text
|
||||
* @return list of labels without overlaps with symbols and other labels by the two fixed position greedy strategy
|
||||
*/
|
||||
private List<PointTextContainer> processTwoPointGreedy(List<PointTextContainer> labels,
|
||||
List<SymbolContainer> symbols, List<PointTextContainer> areaLabels) {
|
||||
List<PointTextContainer> resolutionSet = new ArrayList<PointTextContainer>();
|
||||
// Array for the generated reference positions around the points of
|
||||
// interests
|
||||
ReferencePosition[] refPos = new ReferencePosition[labels.size() * 2];
|
||||
|
||||
// lists that sorts the reference points after the minimum right edge x
|
||||
// position
|
||||
PriorityQueue<ReferencePosition> priorRight = new PriorityQueue<ReferencePosition>(labels.size() * 2
|
||||
+ labels.size() / 10 * 2, ReferencePositionWidthComparator.INSTANCE);
|
||||
// lists that sorts the reference points after the minimum left edge x
|
||||
// position
|
||||
PriorityQueue<ReferencePosition> priorLeft = new PriorityQueue<ReferencePosition>(labels.size() * 2
|
||||
+ labels.size() / 10 * 2, ReferencePositionXComparator.INSTANCE);
|
||||
|
||||
// creates the reference positions
|
||||
for (int z = 0; z < labels.size(); z++) {
|
||||
mLabel = labels.get(z);
|
||||
|
||||
if (mLabel.symbol != null) {
|
||||
refPos[z * 2] = new ReferencePosition(mLabel.x - (mLabel.boundary.width() / 2) - 0.1f,
|
||||
mLabel.y - mLabel.boundary.height() - mStartDistanceToSymbols, z,
|
||||
mLabel.boundary.width(), mLabel.boundary.height(), mLabel.symbol);
|
||||
refPos[z * 2 + 1] = new ReferencePosition(mLabel.x - (mLabel.boundary.width() / 2),
|
||||
mLabel.y + mLabel.symbol.symbol.getHeight() + mStartDistanceToSymbols, z,
|
||||
mLabel.boundary.width(), mLabel.boundary.height(), mLabel.symbol);
|
||||
} else {
|
||||
refPos[z * 2] = new ReferencePosition(mLabel.x - (mLabel.boundary.width() / 2) - 0.1f,
|
||||
mLabel.y, z, mLabel.boundary.width(), mLabel.boundary.height(), null);
|
||||
refPos[z * 2 + 1] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// removes reference positions that overlaps with other symbols or
|
||||
// dependency objects
|
||||
removeNonValidateReferencePosition(refPos, symbols, areaLabels);
|
||||
|
||||
for (int i = 0; i < refPos.length; i++) {
|
||||
mReferencePosition = refPos[i];
|
||||
if (mReferencePosition != null) {
|
||||
priorLeft.add(mReferencePosition);
|
||||
priorRight.add(mReferencePosition);
|
||||
}
|
||||
}
|
||||
|
||||
while (priorRight.size() != 0) {
|
||||
mReferencePosition = priorRight.remove();
|
||||
|
||||
mLabel = labels.get(mReferencePosition.nodeNumber);
|
||||
|
||||
resolutionSet.add(new PointTextContainer(mLabel.text, mReferencePosition.x,
|
||||
mReferencePosition.y, mLabel.paintFront, mLabel.paintBack,
|
||||
mReferencePosition.symbol));
|
||||
|
||||
// Removes the other position that is a possible position for the label
|
||||
// of one point
|
||||
// of interest
|
||||
|
||||
priorRight.remove(refPos[mReferencePosition.nodeNumber * 2 + 1]);
|
||||
|
||||
if (priorRight.size() == 0) {
|
||||
return resolutionSet;
|
||||
}
|
||||
|
||||
priorLeft.remove(mReferencePosition);
|
||||
priorLeft.remove(refPos[mReferencePosition.nodeNumber * 2 + 1]);
|
||||
|
||||
// find overlapping labels and deletes the reference points and delete
|
||||
// them
|
||||
LinkedList<ReferencePosition> linkedRef = new LinkedList<ReferencePosition>();
|
||||
|
||||
while (priorLeft.size() != 0) {
|
||||
if (priorLeft.peek().x < mReferencePosition.x + mReferencePosition.width) {
|
||||
linkedRef.add(priorLeft.remove());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// brute Force collision test (faster then sweep line for a small
|
||||
// amount of
|
||||
// objects)
|
||||
for (int i = 0; i < linkedRef.size(); i++) {
|
||||
if ((linkedRef.get(i).x <= mReferencePosition.x + mReferencePosition.width)
|
||||
&& (linkedRef.get(i).y >= mReferencePosition.y - linkedRef.get(i).height)
|
||||
&& (linkedRef.get(i).y <= mReferencePosition.y + linkedRef.get(i).height)) {
|
||||
priorRight.remove(linkedRef.get(i));
|
||||
linkedRef.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
priorLeft.addAll(linkedRef);
|
||||
}
|
||||
|
||||
return resolutionSet;
|
||||
}
|
||||
|
||||
private void removeEmptySymbolReferences(List<PointTextContainer> nodes, List<SymbolContainer> symbols) {
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
mLabel = nodes.get(i);
|
||||
if (!symbols.contains(mLabel.symbol)) {
|
||||
mLabel.symbol = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The greedy algorithms need possible label positions, to choose the best among them. This method removes the
|
||||
* reference points, that are not validate. Not validate means, that the Reference overlap with another symbol or
|
||||
* label or is outside of the tile.
|
||||
*
|
||||
* @param refPos
|
||||
* list of the potential positions
|
||||
* @param symbols
|
||||
* actual list of the symbols
|
||||
* @param areaLabels
|
||||
* actual list of the area labels
|
||||
*/
|
||||
private void removeNonValidateReferencePosition(ReferencePosition[] refPos, List<SymbolContainer> symbols,
|
||||
List<PointTextContainer> areaLabels) {
|
||||
int dis = mLabelDistanceToSymbol;
|
||||
|
||||
for (int i = 0; i < symbols.size(); i++) {
|
||||
mSymbolContainer = symbols.get(i);
|
||||
mRect1.set((int) mSymbolContainer.x - dis, (int) mSymbolContainer.y - dis,
|
||||
(int) mSymbolContainer.x + mSymbolContainer.symbol.getWidth() + dis,
|
||||
(int) mSymbolContainer.y + mSymbolContainer.symbol.getHeight() + dis);
|
||||
|
||||
for (int y = 0; y < refPos.length; y++) {
|
||||
if (refPos[y] != null) {
|
||||
|
||||
mRect2.set((int) refPos[y].x, (int) (refPos[y].y - refPos[y].height),
|
||||
(int) (refPos[y].x + refPos[y].width), (int) (refPos[y].y));
|
||||
|
||||
if (android.graphics.Rect.intersects(mRect2, mRect1)) {
|
||||
refPos[y] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dis = mLabelDistanceToLabel;
|
||||
|
||||
for (PointTextContainer areaLabel : areaLabels) {
|
||||
|
||||
mRect1.set((int) areaLabel.x - dis, (int) areaLabel.y - areaLabel.boundary.height() - dis,
|
||||
(int) areaLabel.x + areaLabel.boundary.width() + dis, (int) areaLabel.y + dis);
|
||||
|
||||
for (int y = 0; y < refPos.length; y++) {
|
||||
if (refPos[y] != null) {
|
||||
|
||||
mRect2.set((int) refPos[y].x, (int) (refPos[y].y - refPos[y].height),
|
||||
(int) (refPos[y].x + refPos[y].width), (int) (refPos[y].y));
|
||||
|
||||
if (android.graphics.Rect.intersects(mRect2, mRect1)) {
|
||||
refPos[y] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDependencyCache.removeReferencePointsFromDependencyCache(refPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the area labels, that are not visible in the actual tile.
|
||||
*
|
||||
* @param areaLabels
|
||||
* area Labels from the actual tile
|
||||
*/
|
||||
private void removeOutOfTileAreaLabels(List<PointTextContainer> areaLabels) {
|
||||
for (int i = 0; i < areaLabels.size(); i++) {
|
||||
mLabel = areaLabels.get(i);
|
||||
|
||||
if (mLabel.x > Tile.TILE_SIZE) {
|
||||
areaLabels.remove(i);
|
||||
|
||||
i--;
|
||||
} else if (mLabel.y - mLabel.boundary.height() > Tile.TILE_SIZE) {
|
||||
areaLabels.remove(i);
|
||||
|
||||
i--;
|
||||
} else if (mLabel.x + mLabel.boundary.width() < 0.0f) {
|
||||
areaLabels.remove(i);
|
||||
|
||||
i--;
|
||||
} else if (mLabel.y + mLabel.boundary.height() < 0.0f) {
|
||||
areaLabels.remove(i);
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the labels, that are not visible in the actual tile.
|
||||
*
|
||||
* @param labels
|
||||
* Labels from the actual tile
|
||||
*/
|
||||
private void removeOutOfTileLabels(List<PointTextContainer> labels) {
|
||||
for (int i = 0; i < labels.size();) {
|
||||
mLabel = labels.get(i);
|
||||
|
||||
if (mLabel.x - mLabel.boundary.width() / 2 > Tile.TILE_SIZE) {
|
||||
labels.remove(i);
|
||||
mLabel = null;
|
||||
} else if (mLabel.y - mLabel.boundary.height() > Tile.TILE_SIZE) {
|
||||
labels.remove(i);
|
||||
mLabel = null;
|
||||
} else if ((mLabel.x - mLabel.boundary.width() / 2 + mLabel.boundary.width()) < 0.0f) {
|
||||
labels.remove(i);
|
||||
mLabel = null;
|
||||
} else if (mLabel.y < 0.0f) {
|
||||
labels.remove(i);
|
||||
mLabel = null;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the Symbols, that are not visible in the actual tile.
|
||||
*
|
||||
* @param symbols
|
||||
* Symbols from the actual tile
|
||||
*/
|
||||
private void removeOutOfTileSymbols(List<SymbolContainer> symbols) {
|
||||
for (int i = 0; i < symbols.size();) {
|
||||
mSymbolContainer = symbols.get(i);
|
||||
|
||||
if (mSymbolContainer.x > Tile.TILE_SIZE) {
|
||||
symbols.remove(i);
|
||||
} else if (mSymbolContainer.y > Tile.TILE_SIZE) {
|
||||
symbols.remove(i);
|
||||
} else if (mSymbolContainer.x + mSymbolContainer.symbol.getWidth() < 0.0f) {
|
||||
symbols.remove(i);
|
||||
} else if (mSymbolContainer.y + mSymbolContainer.symbol.getHeight() < 0.0f) {
|
||||
symbols.remove(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes all the area labels, that overlap each other. So that the output is collision free
|
||||
*
|
||||
* @param areaLabels
|
||||
* area labels from the actual tile
|
||||
*/
|
||||
private void removeOverlappingAreaLabels(List<PointTextContainer> areaLabels) {
|
||||
int dis = mLabelDistanceToLabel;
|
||||
|
||||
for (int x = 0; x < areaLabels.size(); x++) {
|
||||
mLabel = areaLabels.get(x);
|
||||
mRect1.set((int) mLabel.x - dis, (int) mLabel.y - dis,
|
||||
(int) (mLabel.x + mLabel.boundary.width()) + dis,
|
||||
(int) (mLabel.y + mLabel.boundary.height() + dis));
|
||||
|
||||
for (int y = x + 1; y < areaLabels.size(); y++) {
|
||||
if (y != x) {
|
||||
mLabel = areaLabels.get(y);
|
||||
mRect2.set((int) mLabel.x, (int) mLabel.y,
|
||||
(int) (mLabel.x + mLabel.boundary.width()),
|
||||
(int) (mLabel.y + mLabel.boundary.height()));
|
||||
|
||||
if (android.graphics.Rect.intersects(mRect1, mRect2)) {
|
||||
areaLabels.remove(y);
|
||||
|
||||
y--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the the symbols that overlap with area labels.
|
||||
*
|
||||
* @param symbols
|
||||
* list of symbols
|
||||
* @param pTC
|
||||
* list of labels
|
||||
*/
|
||||
private void removeOverlappingSymbolsWithAreaLabels(List<SymbolContainer> symbols, List<PointTextContainer> pTC) {
|
||||
int dis = mLabelDistanceToSymbol;
|
||||
|
||||
for (int x = 0; x < pTC.size(); x++) {
|
||||
mLabel = pTC.get(x);
|
||||
|
||||
mRect1.set((int) mLabel.x - dis, (int) (mLabel.y - mLabel.boundary.height()) - dis,
|
||||
(int) (mLabel.x + mLabel.boundary.width() + dis), (int) (mLabel.y + dis));
|
||||
|
||||
for (int y = 0; y < symbols.size(); y++) {
|
||||
mSymbolContainer = symbols.get(y);
|
||||
|
||||
mRect2.set((int) mSymbolContainer.x, (int) mSymbolContainer.y,
|
||||
(int) (mSymbolContainer.x + mSymbolContainer.symbol.getWidth()),
|
||||
(int) (mSymbolContainer.y + mSymbolContainer.symbol.getHeight()));
|
||||
|
||||
if (android.graphics.Rect.intersects(mRect1, mRect2)) {
|
||||
symbols.remove(y);
|
||||
y--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getLabelDistanceToLabel() {
|
||||
return mLabelDistanceToLabel;
|
||||
}
|
||||
|
||||
int getLabelDistanceToSymbol() {
|
||||
return mLabelDistanceToSymbol;
|
||||
}
|
||||
|
||||
int getPlacementOption() {
|
||||
return PLACEMENT_MODEL;
|
||||
}
|
||||
|
||||
int getStartDistanceToSymbols() {
|
||||
return mStartDistanceToSymbols;
|
||||
}
|
||||
|
||||
int getSymbolDistanceToSymbol() {
|
||||
return mSymbolDistanceToSymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inputs are all the label and symbol objects of the current tile. The output is overlap free label and symbol
|
||||
* placement with the greedy strategy. The placement model is either the two fixed point or the four fixed point
|
||||
* model.
|
||||
*
|
||||
* @param labels
|
||||
* labels from the current tile.
|
||||
* @param symbols
|
||||
* symbols of the current tile.
|
||||
* @param areaLabels
|
||||
* area labels from the current tile.
|
||||
* @param cT
|
||||
* current tile with the x,y- coordinates and the zoom level.
|
||||
* @return the processed list of labels.
|
||||
*/
|
||||
List<PointTextContainer> placeLabels(List<PointTextContainer> labels, List<SymbolContainer> symbols,
|
||||
List<PointTextContainer> areaLabels, Tile cT) {
|
||||
List<PointTextContainer> returnLabels = labels;
|
||||
mDependencyCache.generateTileAndDependencyOnTile(cT);
|
||||
|
||||
preprocessAreaLabels(areaLabels);
|
||||
|
||||
preprocessLabels(returnLabels);
|
||||
|
||||
preprocessSymbols(symbols);
|
||||
|
||||
removeEmptySymbolReferences(returnLabels, symbols);
|
||||
|
||||
removeOverlappingSymbolsWithAreaLabels(symbols, areaLabels);
|
||||
|
||||
mDependencyCache.removeOverlappingObjectsWithDependencyOnTile(returnLabels, areaLabels, symbols);
|
||||
|
||||
if (!returnLabels.isEmpty()) {
|
||||
switch (PLACEMENT_MODEL) {
|
||||
case 0:
|
||||
returnLabels = processTwoPointGreedy(returnLabels, symbols, areaLabels);
|
||||
break;
|
||||
case 1:
|
||||
returnLabels = processFourPointGreedy(returnLabels, symbols, areaLabels);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mDependencyCache.fillDependencyOnTile(returnLabels, symbols, areaLabels);
|
||||
|
||||
return returnLabels;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes all the Symbols, that overlap each other. So that the output is collision free.
|
||||
*
|
||||
* @param symbols
|
||||
* symbols from the actual tile
|
||||
*/
|
||||
void removeOverlappingSymbols(List<SymbolContainer> symbols) {
|
||||
int dis = mSymbolDistanceToSymbol;
|
||||
|
||||
for (int x = 0; x < symbols.size(); x++) {
|
||||
mSymbolContainer = symbols.get(x);
|
||||
mRect1.set((int) mSymbolContainer.x - dis, (int) mSymbolContainer.y - dis,
|
||||
(int) mSymbolContainer.x + mSymbolContainer.symbol.getWidth() + dis,
|
||||
(int) mSymbolContainer.y + mSymbolContainer.symbol.getHeight() + dis);
|
||||
|
||||
for (int y = x + 1; y < symbols.size(); y++) {
|
||||
if (y != x) {
|
||||
mSymbolContainer = symbols.get(y);
|
||||
mRect2.set((int) mSymbolContainer.x, (int) mSymbolContainer.y,
|
||||
(int) mSymbolContainer.x + mSymbolContainer.symbol.getWidth(),
|
||||
(int) mSymbolContainer.y + mSymbolContainer.symbol.getHeight());
|
||||
|
||||
if (android.graphics.Rect.intersects(mRect2, mRect1)) {
|
||||
symbols.remove(y);
|
||||
y--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLabelDistanceToLabel(int labelDistanceToLabel) {
|
||||
mLabelDistanceToLabel = labelDistanceToLabel;
|
||||
}
|
||||
|
||||
void setLabelDistanceToSymbol(int labelDistanceToSymbol) {
|
||||
mLabelDistanceToSymbol = labelDistanceToSymbol;
|
||||
}
|
||||
|
||||
void setStartDistanceToSymbols(int startDistanceToSymbols) {
|
||||
mStartDistanceToSymbols = startDistanceToSymbols;
|
||||
}
|
||||
|
||||
void setSymbolDistanceToSymbol(int symbolDistanceToSymbol) {
|
||||
mSymbolDistanceToSymbol = symbolDistanceToSymbol;
|
||||
}
|
||||
}
|
||||
63
src/org/mapsforge/android/swrenderer/LayerContainer.java
Normal file
63
src/org/mapsforge/android/swrenderer/LayerContainer.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import android.graphics.Paint;
|
||||
|
||||
class LayerContainer {
|
||||
|
||||
final LevelContainer[] mLevels;
|
||||
final boolean[] mLevelActive;
|
||||
boolean mActive;
|
||||
|
||||
LayerContainer(int levels) {
|
||||
mLevels = new LevelContainer[levels];
|
||||
mLevelActive = new boolean[levels];
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
List<ShapeContainer> add(int level, ShapeContainer shapeContainer, Paint paint) {
|
||||
mActive = true;
|
||||
LevelContainer levelContainer = mLevels[level];
|
||||
if (levelContainer == null) {
|
||||
levelContainer = new LevelContainer();
|
||||
mLevels[level] = levelContainer;
|
||||
}
|
||||
|
||||
levelContainer.add(shapeContainer, paint);
|
||||
|
||||
mLevelActive[level] = true;
|
||||
|
||||
return levelContainer.mShapeContainers;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (!mActive)
|
||||
return;
|
||||
|
||||
mActive = false;
|
||||
|
||||
for (int level = mLevels.length - 1; level >= 0; level--) {
|
||||
if (mLevelActive[level]) {
|
||||
LevelContainer levelContainer = mLevels[level];
|
||||
mLevelActive[level] = false;
|
||||
levelContainer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/org/mapsforge/android/swrenderer/LevelContainer.java
Normal file
45
src/org/mapsforge/android/swrenderer/LevelContainer.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
import android.graphics.Paint;
|
||||
|
||||
class LevelContainer {
|
||||
final ArrayList<ShapeContainer> mShapeContainers;
|
||||
Paint[] mPaint;
|
||||
|
||||
LevelContainer() {
|
||||
mShapeContainers = new ArrayList<ShapeContainer>(20);
|
||||
mPaint = new Paint[2];
|
||||
}
|
||||
|
||||
void add(ShapeContainer shapeContainer, Paint paint) {
|
||||
if (mPaint[0] == null)
|
||||
mPaint[0] = paint;
|
||||
else if (mPaint[0] != paint)
|
||||
mPaint[1] = paint;
|
||||
|
||||
mShapeContainers.add(shapeContainer);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mShapeContainers.clear();
|
||||
mPaint[0] = null;
|
||||
mPaint[1] = null;
|
||||
}
|
||||
}
|
||||
643
src/org/mapsforge/android/swrenderer/MapRenderer.java
Normal file
643
src/org/mapsforge/android/swrenderer/MapRenderer.java
Normal file
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import org.mapsforge.android.DebugSettings;
|
||||
import org.mapsforge.android.MapView;
|
||||
import org.mapsforge.android.mapgenerator.JobParameters;
|
||||
import org.mapsforge.android.mapgenerator.MapGenerator;
|
||||
import org.mapsforge.android.mapgenerator.MapGeneratorJob;
|
||||
import org.mapsforge.android.mapgenerator.MapWorker;
|
||||
import org.mapsforge.android.mapgenerator.TileCacheKey;
|
||||
import org.mapsforge.android.mapgenerator.TileDistanceSort;
|
||||
import org.mapsforge.android.utils.GlUtils;
|
||||
import org.mapsforge.core.MapPosition;
|
||||
import org.mapsforge.core.MercatorProjection;
|
||||
import org.mapsforge.core.Tile;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MapRenderer implements org.mapsforge.android.MapRenderer {
|
||||
// private static String TAG = "MapRenderer";
|
||||
|
||||
private static final int FLOAT_SIZE_BYTES = 4;
|
||||
private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
|
||||
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
|
||||
|
||||
private int mProgram;
|
||||
private int muMVPMatrixHandle;
|
||||
private int maPositionHandle;
|
||||
private int maTextureHandle;
|
||||
private int muScaleHandle;
|
||||
private FloatBuffer mVertices;
|
||||
private float[] mMatrix = new float[16];
|
||||
|
||||
private int mWidth, mHeight;
|
||||
private double mDrawX, mDrawY;
|
||||
private long mTileX, mTileY;
|
||||
private float mMapScale;
|
||||
private DebugSettings mDebugSettings;
|
||||
private JobParameters mJobParameter;
|
||||
private MapPosition mMapPosition, mPrevMapPosition;
|
||||
|
||||
private ArrayList<MapGeneratorJob> mJobList;
|
||||
|
||||
ArrayList<Integer> mTextures;
|
||||
MapWorker mMapWorker;
|
||||
MapView mMapView;
|
||||
|
||||
GLMapTile[] currentTiles;
|
||||
GLMapTile[] newTiles;
|
||||
int currentTileCnt = 0;
|
||||
|
||||
private TileCacheKey mTileCacheKey;
|
||||
private LinkedHashMap<TileCacheKey, GLMapTile> mTiles;
|
||||
private ArrayList<GLMapTile> mTileList;
|
||||
|
||||
private boolean processedTile = true;
|
||||
|
||||
private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
|
||||
|
||||
private boolean mInitial;
|
||||
|
||||
private final TileDistanceSort tileDistanceSort = new TileDistanceSort();
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public MapRenderer(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mMapWorker = mapView.getMapWorker();
|
||||
mDebugSettings = mapView.getDebugSettings();
|
||||
mMapScale = 1;
|
||||
|
||||
float[] vertices = {
|
||||
0, 0, 0, 0, 0.5f,
|
||||
0, 1, 0, 0, 0,
|
||||
1, 0, 0, 0.5f, 0.5f,
|
||||
1, 1, 0, 0.5f, 0 };
|
||||
|
||||
mVertices = ByteBuffer.allocateDirect(4 * TRIANGLE_VERTICES_DATA_STRIDE_BYTES)
|
||||
.order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
mVertices.put(vertices);
|
||||
|
||||
mTextures = new ArrayList<Integer>();
|
||||
mJobList = new ArrayList<MapGeneratorJob>();
|
||||
|
||||
mTiles = new LinkedHashMap<TileCacheKey, GLMapTile>(100);
|
||||
mTileList = new ArrayList<GLMapTile>();
|
||||
|
||||
mTileCacheKey = new TileCacheKey();
|
||||
mInitial = true;
|
||||
}
|
||||
|
||||
private void limitCache(byte zoom, int remove) {
|
||||
long x = mTileX;
|
||||
long y = mTileY;
|
||||
int diff;
|
||||
|
||||
for (GLMapTile t : mTileList) {
|
||||
|
||||
diff = (t.zoomLevel - zoom);
|
||||
|
||||
if (diff != 0)
|
||||
{
|
||||
float z = (diff > 0) ? (1 << diff) : 1.0f / (1 << -diff);
|
||||
t.distance = (long) (Math.abs((t.tileX) * z - x) + Math.abs((t.tileY) * z - y));
|
||||
t.distance *= 2 * diff * diff;
|
||||
} else {
|
||||
t.distance = (Math.abs(t.tileX - x) + Math.abs(t.tileY - y));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(mTileList, tileDistanceSort);
|
||||
|
||||
for (int j = mTileList.size() - 1, cnt = 0; cnt < remove; j--, cnt++) {
|
||||
GLMapTile t = mTileList.remove(j);
|
||||
|
||||
mTileCacheKey.set(t.tileX, t.tileY, t.zoomLevel);
|
||||
mTiles.remove(mTileCacheKey);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (t.child[i] != null)
|
||||
t.child[i].parent = null;
|
||||
}
|
||||
if (t.parent != null) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (t.parent.child[i] == t)
|
||||
t.parent.child[i] = null;
|
||||
}
|
||||
}
|
||||
if (t.hasTexture()) {
|
||||
synchronized (mTextures) {
|
||||
mTextures.add(new Integer(t.getTexture()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean updateVisibleList(long x, long y, byte zoomLevel) {
|
||||
float scale = mMapPosition.scale;
|
||||
double add = 1.0f / scale;
|
||||
int offsetX = (int) ((mWidth >> 1) * add);
|
||||
int offsetY = (int) ((mHeight >> 1) * add);
|
||||
|
||||
long pixelRight = x + offsetX;
|
||||
long pixelBottom = y + offsetY;
|
||||
long pixelLeft = x - offsetX;
|
||||
long pixelTop = y - offsetY;
|
||||
|
||||
long tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel);
|
||||
long tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel);
|
||||
long tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel);
|
||||
long tileBottom = MercatorProjection.pixelYToTileY(pixelBottom, zoomLevel);
|
||||
|
||||
mJobList.clear();
|
||||
|
||||
MapGenerator mapGenerator = mMapView.getMapGenerator();
|
||||
int tiles = 0;
|
||||
for (long tileY = tileTop - 1; tileY <= tileBottom + 1; tileY++) {
|
||||
for (long tileX = tileLeft - 1; tileX <= tileRight + 1; tileX++) {
|
||||
|
||||
GLMapTile tile = mTiles.get(mTileCacheKey.set(tileX, tileY, zoomLevel));
|
||||
|
||||
if (tile == null) {
|
||||
tile = new GLMapTile(tileX, tileY, zoomLevel);
|
||||
TileCacheKey key = new TileCacheKey(mTileCacheKey);
|
||||
mTiles.put(key, tile);
|
||||
|
||||
mTileCacheKey.set((tileX >> 1), (tileY >> 1), (byte) (zoomLevel - 1));
|
||||
tile.parent = mTiles.get(mTileCacheKey);
|
||||
|
||||
long xx = tileX << 1;
|
||||
long yy = tileY << 1;
|
||||
byte z = (byte) (zoomLevel + 1);
|
||||
|
||||
tile.child[0] = mTiles.get(mTileCacheKey.set(xx, yy, z));
|
||||
tile.child[1] = mTiles.get(mTileCacheKey.set(xx + 1, yy, z));
|
||||
tile.child[2] = mTiles.get(mTileCacheKey.set(xx, yy + 1, z));
|
||||
tile.child[3] = mTiles.get(mTileCacheKey.set(xx + 1, yy + 1, z));
|
||||
|
||||
mTileList.add(tile);
|
||||
}
|
||||
|
||||
newTiles[tiles++] = tile;
|
||||
|
||||
if (!tile.isDrawn || (tile.getScale() != scale)) {
|
||||
tile.isLoading = true;
|
||||
// approximation for TileScheduler
|
||||
if (tileY < tileTop || tileY > tileBottom || tileX < tileLeft || tileX > tileRight)
|
||||
tile.isVisible = false;
|
||||
else
|
||||
tile.isVisible = true;
|
||||
|
||||
MapGeneratorJob job = new MapGeneratorJob(tile, mapGenerator,
|
||||
mJobParameter, mDebugSettings);
|
||||
job.setScale(scale);
|
||||
mJobList.add(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
|
||||
limitCache(zoomLevel, (mTiles.size() - 200));
|
||||
|
||||
for (int i = 0; i < tiles; i++)
|
||||
currentTiles[i] = newTiles[i];
|
||||
currentTileCnt = tiles;
|
||||
|
||||
mDrawX = x;
|
||||
mDrawY = y;
|
||||
mMapScale = scale;
|
||||
}
|
||||
|
||||
if (mJobList.size() > 0) {
|
||||
mMapView.getJobQueue().setJobs(mJobList);
|
||||
synchronized (mMapWorker) {
|
||||
mMapWorker.notify();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public synchronized void redrawTiles(boolean clear) {
|
||||
|
||||
boolean update = false;
|
||||
|
||||
mMapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
|
||||
long x = (long) MercatorProjection.longitudeToPixelX(mMapPosition.geoPoint.getLongitude(),
|
||||
mMapPosition.zoomLevel);
|
||||
long y = (long) MercatorProjection
|
||||
.latitudeToPixelY(mMapPosition.geoPoint.getLatitude(), mMapPosition.zoomLevel);
|
||||
|
||||
long tileX = MercatorProjection.pixelXToTileX(x, mMapPosition.zoomLevel);
|
||||
long tileY = MercatorProjection.pixelYToTileY(y, mMapPosition.zoomLevel);
|
||||
float scale = mMapPosition.scale;
|
||||
|
||||
if (mInitial) {
|
||||
mInitial = false;
|
||||
mPrevMapPosition = mMapPosition;
|
||||
mTileX = tileX;
|
||||
mTileY = tileY;
|
||||
update = true;
|
||||
} else if (mPrevMapPosition.zoomLevel != mMapPosition.zoomLevel) {
|
||||
update = true;
|
||||
} else if (mMapScale != scale) {
|
||||
update = true;
|
||||
} else if (tileX != mTileX || tileY != mTileY) {
|
||||
update = true;
|
||||
}
|
||||
|
||||
mTileX = tileX;
|
||||
mTileY = tileY;
|
||||
|
||||
if (update) {
|
||||
// do not change list while drawing
|
||||
// synchronized (this) {
|
||||
mPrevMapPosition = mMapPosition;
|
||||
updateVisibleList(x, y, mMapPosition.zoomLevel);
|
||||
}
|
||||
else {
|
||||
synchronized (this) {
|
||||
mDrawX = x;
|
||||
mDrawY = y;
|
||||
}
|
||||
}
|
||||
|
||||
mMapView.requestRender();
|
||||
}
|
||||
|
||||
private MapGeneratorJob mMapGeneratorJob = null;
|
||||
|
||||
@Override
|
||||
public boolean passTile(MapGeneratorJob mapGeneratorJob) {
|
||||
|
||||
mMapGeneratorJob = mapGeneratorJob;
|
||||
processedTile = false;
|
||||
mMapView.requestRender();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean drawTile(GLMapTile tile, int level, float height) {
|
||||
|
||||
// do not recurse more than two parents
|
||||
if (level > 2)
|
||||
return true;
|
||||
|
||||
if (!tile.hasTexture()) {
|
||||
// draw parent below current zoom level tiles
|
||||
float h = height > 0 ? height * 2 : 0.1f;
|
||||
|
||||
if (level <= 2 && tile.parent != null)
|
||||
return drawTile(tile.parent, level + 1, h);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float z = 1;
|
||||
double drawX = mDrawX;
|
||||
double drawY = mDrawY;
|
||||
// translate all pixel coordinates * 'zoom factor difference'
|
||||
// TODO clip tile when drawing parent
|
||||
int diff = tile.zoomLevel - mMapPosition.zoomLevel;
|
||||
if (diff != 0) {
|
||||
if (diff > 0) {
|
||||
z = (1 << diff);
|
||||
} else {
|
||||
z = 1.0f / (1 << -diff);
|
||||
}
|
||||
drawX = MercatorProjection
|
||||
.longitudeToPixelX(mMapPosition.geoPoint.getLongitude(), tile.zoomLevel);
|
||||
drawY = MercatorProjection
|
||||
.latitudeToPixelY(mMapPosition.geoPoint.getLatitude(), tile.zoomLevel);
|
||||
|
||||
}
|
||||
|
||||
float mapScale = mMapScale / z;
|
||||
int tileSize = Tile.TILE_SIZE;
|
||||
float size = tileSize * mapScale;
|
||||
|
||||
float x = (float) ((tile.pixelX) - drawX) * mapScale;
|
||||
float y = (float) ((tile.pixelY + tileSize) - drawY) * mapScale;
|
||||
|
||||
if (x + size < -mWidth / 2 || x > mWidth / 2) {
|
||||
// Log.i(TAG, tile + " skip X " + x + " " + y);
|
||||
tile.isVisible = false;
|
||||
return true;
|
||||
}
|
||||
if (y < -mHeight / 2 || y - size > mHeight / 2) {
|
||||
// Log.i(TAG, tile + " skip Y " + x + " " + y);
|
||||
tile.isVisible = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Log.i(TAG, tile + " draw " + x + " " + y);
|
||||
tile.isVisible = true;
|
||||
|
||||
// set drawn tile scale (texture size)
|
||||
GLES20.glUniform1f(muScaleHandle, tile.getScale());
|
||||
|
||||
Matrix.setIdentityM(mMatrix, 0);
|
||||
// map tile GL coordinates to screen coordinates
|
||||
Matrix.scaleM(mMatrix, 0, 2.0f * (tileSize * z) / mWidth, 2.0f * (tileSize * z) / mHeight, 1);
|
||||
|
||||
// scale tile
|
||||
Matrix.scaleM(mMatrix, 0, mapScale / z, mapScale / z, 1);
|
||||
|
||||
// translate tile
|
||||
Matrix.translateM(mMatrix, 0, (x / size), -(y / size), height);
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.getTexture());
|
||||
// GlUtils.checkGlError("glBindTexture");
|
||||
|
||||
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMatrix, 0);
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 glUnused) {
|
||||
boolean loadedTexture = false;
|
||||
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
|
||||
GLES20.glClearColor(0.95f, 0.95f, 0.94f, 1.0f);
|
||||
// GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLES20.glUseProgram(mProgram);
|
||||
GlUtils.checkGlError("glUseProgram");
|
||||
|
||||
mVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
|
||||
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
|
||||
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mVertices);
|
||||
GlUtils.checkGlError("glVertexAttribPointer maPosition");
|
||||
|
||||
GLES20.glEnableVertexAttribArray(maPositionHandle);
|
||||
GlUtils.checkGlError("glEnableVertexAttribArray maPositionHandle");
|
||||
|
||||
mVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
|
||||
GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
|
||||
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mVertices);
|
||||
GlUtils.checkGlError("glVertexAttribPointer maTextureHandle");
|
||||
|
||||
GLES20.glEnableVertexAttribArray(maTextureHandle);
|
||||
GlUtils.checkGlError("glEnableVertexAttribArray maTextureHandle");
|
||||
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
||||
|
||||
GLMapTile tile, child, child2;
|
||||
|
||||
GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
|
||||
|
||||
// lock position and currentTiles while drawing
|
||||
synchronized (this) {
|
||||
if (mMapGeneratorJob != null) {
|
||||
|
||||
tile = (GLMapTile) mMapGeneratorJob.tile;
|
||||
// TODO tile bitmaps texture to smaller parts avoiding uploading full
|
||||
// bitmap when not necessary
|
||||
if (tile.getTexture() >= 0) {
|
||||
// reuse tile texture
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.getTexture());
|
||||
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mMapGeneratorJob.getBitmap());
|
||||
} else if (mTextures.size() > 0) {
|
||||
// reuse texture from previous tiles
|
||||
Integer texture;
|
||||
texture = mTextures.remove(mTextures.size() - 1);
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture.intValue());
|
||||
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mMapGeneratorJob.getBitmap());
|
||||
tile.setTexture(texture.intValue());
|
||||
} else {
|
||||
// create texture
|
||||
tile.setTexture(GlUtils.loadTextures(mMapGeneratorJob.getBitmap()));
|
||||
}
|
||||
|
||||
tile.setScale(mMapGeneratorJob.getScale());
|
||||
tile.isDrawn = true;
|
||||
tile.isLoading = false;
|
||||
|
||||
mMapGeneratorJob = null;
|
||||
processedTile = true;
|
||||
loadedTexture = true;
|
||||
}
|
||||
int tileSize = (int) (Tile.TILE_SIZE * mMapScale);
|
||||
int hWidth = mWidth >> 1;
|
||||
int hHeight = mHeight >> 1;
|
||||
for (int i = 0, n = currentTileCnt; i < n; i++) {
|
||||
tile = currentTiles[i];
|
||||
|
||||
float x = (float) (tile.pixelX - mDrawX);
|
||||
float y = (float) (tile.pixelY - mDrawY);
|
||||
|
||||
// clip rendering to tile boundaries
|
||||
GLES20.glScissor(
|
||||
hWidth + (int) (x * mMapScale) - 2,
|
||||
hHeight - (int) (y * mMapScale) - tileSize - 2,
|
||||
tileSize + 4, tileSize + 4);
|
||||
|
||||
if (drawTile(tile, 0, 0.0f))
|
||||
continue;
|
||||
|
||||
// or two zoom level above
|
||||
for (int k = 0; k < 4; k++) {
|
||||
if (((child = tile.child[k]) != null)) {
|
||||
|
||||
if (drawTile(child, 2, 0.1f))
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
if ((child2 = child.child[j]) != null)
|
||||
drawTile(child2, 2, 0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loadedTexture) {
|
||||
synchronized (mMapWorker) {
|
||||
mMapWorker.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
int tiles = (mWidth / Tile.TILE_SIZE + 4) * (mHeight / Tile.TILE_SIZE + 4);
|
||||
currentTiles = new GLMapTile[tiles];
|
||||
newTiles = new GLMapTile[tiles];
|
||||
|
||||
GLES20.glViewport(0, 0, width, height);
|
||||
|
||||
mDebugSettings = mMapView.getDebugSettings();
|
||||
mJobParameter = mMapView.getJobParameters();
|
||||
|
||||
mTiles.clear();
|
||||
mTileList.clear();
|
||||
mTextures.clear();
|
||||
mInitial = true;
|
||||
mMapView.redrawTiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
|
||||
|
||||
mProgram = GlUtils.createProgram(mVertexShader, mFragmentShader);
|
||||
if (mProgram == 0) {
|
||||
return;
|
||||
}
|
||||
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
|
||||
GlUtils.checkGlError("glGetAttribLocation aPosition");
|
||||
if (maPositionHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for aPosition");
|
||||
}
|
||||
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
|
||||
GlUtils.checkGlError("glGetAttribLocation aTextureCoord");
|
||||
if (maTextureHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for aTextureCoord");
|
||||
}
|
||||
muScaleHandle = GLES20.glGetUniformLocation(mProgram, "uScale");
|
||||
GlUtils.checkGlError("glGetAttribLocation uScale");
|
||||
if (muScaleHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for uScale");
|
||||
}
|
||||
|
||||
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
|
||||
GlUtils.checkGlError("glGetUniformLocation uMVPMatrix");
|
||||
if (muMVPMatrixHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for uMVPMatrix");
|
||||
}
|
||||
|
||||
// GLES20.glEnable(GLES20.GL_DEPTH_TEST);
|
||||
// GLES20.glDepthFunc(GLES20.GL_LEQUAL);
|
||||
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
|
||||
|
||||
GLES20.glCullFace(GLES20.GL_BACK);
|
||||
GLES20.glFrontFace(GLES20.GL_CW);
|
||||
GLES20.glEnable(GLES20.GL_CULL_FACE);
|
||||
|
||||
}
|
||||
|
||||
private final String mVertexShader = "precision highp float;\n" +
|
||||
"uniform float uScale;\n" +
|
||||
"uniform mat4 uMVPMatrix;\n" + "attribute vec4 aPosition;\n" +
|
||||
"attribute vec2 aTextureCoord;\n" +
|
||||
"varying vec2 vTextureCoord;\n" + "void main() {\n" +
|
||||
" gl_Position = uMVPMatrix * aPosition;\n" +
|
||||
" vTextureCoord = aTextureCoord * uScale;\n" +
|
||||
"}\n";
|
||||
|
||||
private final String mFragmentShader = "precision highp float;\n" +
|
||||
"uniform float uScale;\n" +
|
||||
"varying vec2 vTextureCoord;\n" +
|
||||
"uniform sampler2D sTexture;\n" +
|
||||
"void main() {\n" +
|
||||
" gl_FragColor = texture2D(sTexture, vTextureCoord); \n" +
|
||||
"}\n";
|
||||
|
||||
@Override
|
||||
public boolean processedTile() {
|
||||
return processedTile;
|
||||
}
|
||||
}
|
||||
|
||||
// private void limitCache(long top, long bottom, long left, long right, byte zoom, int remove) {
|
||||
// int cnt = 0;
|
||||
// TileCacheKey[] keys = new TileCacheKey[remove];
|
||||
//
|
||||
// for (Entry<TileCacheKey, GLMapTile> e : mTiles.entrySet()) {
|
||||
// GLMapTile t = e.getValue();
|
||||
// if (t.zoomLevel == zoom && t.tileX >= left && t.tileX <= right &&
|
||||
// t.tileY >= top && t.tileY <= bottom)
|
||||
// continue;
|
||||
//
|
||||
// if (t.zoomLevel + 1 == zoom) {
|
||||
// boolean found = false;
|
||||
// for (int i = 0; i < 4; i++) {
|
||||
// GLMapTile c = t.child[i];
|
||||
// if (c != null && !c.hasTexture() && c.tileX >= left && c.tileX <= right &&
|
||||
// c.tileY >= top && c.tileY <= bottom) {
|
||||
// found = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (found)
|
||||
// continue;
|
||||
// }
|
||||
// if (t.zoomLevel - 1 == zoom) {
|
||||
// GLMapTile p = t.parent;
|
||||
// if (p != null && !p.hasTexture() && p.tileX >= left && p.tileX <= right &&
|
||||
// p.tileY >= top && p.tileY <= bottom) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// keys[cnt++] = e.getKey();
|
||||
//
|
||||
// if (cnt == remove)
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// for (TileCacheKey key : keys) {
|
||||
// GLMapTile t = mTiles.remove(key);
|
||||
// if (t == null)
|
||||
// continue;
|
||||
//
|
||||
// for (int i = 0; i < 4; i++) {
|
||||
// if (t.child[i] != null)
|
||||
// t.child[i].parent = null;
|
||||
// }
|
||||
// if (t.parent != null) {
|
||||
// for (int i = 0; i < 4; i++) {
|
||||
// if (t.parent.child[i] == t)
|
||||
// t.parent.child[i] = null;
|
||||
// }
|
||||
// }
|
||||
// if (t.hasTexture()) {
|
||||
// synchronized (mTextures) {
|
||||
// mTextures.add(new Integer(t.getTexture()));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
115
src/org/mapsforge/android/swrenderer/PointTextContainer.java
Normal file
115
src/org/mapsforge/android/swrenderer/PointTextContainer.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
|
||||
class PointTextContainer {
|
||||
final Rect boundary;
|
||||
final Paint paintBack;
|
||||
final Paint paintFront;
|
||||
SymbolContainer symbol;
|
||||
final String text;
|
||||
float x;
|
||||
float y;
|
||||
|
||||
/**
|
||||
* Create a new point container, that holds the x-y coordinates of a point, a text variable and one paint objects.
|
||||
*
|
||||
* @param text
|
||||
* the text of the point.
|
||||
* @param x
|
||||
* the x coordinate of the point.
|
||||
* @param y
|
||||
* the y coordinate of the point.
|
||||
* @param paintFront
|
||||
* the paintFront for the point.
|
||||
*/
|
||||
PointTextContainer(String text, float x, float y, Paint paintFront) {
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.paintFront = paintFront;
|
||||
this.paintBack = null;
|
||||
this.symbol = null;
|
||||
|
||||
this.boundary = new Rect();
|
||||
paintFront.getTextBounds(text, 0, text.length(), this.boundary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new point container, that holds the x-y coordinates of a point, a text variable and two paint objects.
|
||||
*
|
||||
* @param text
|
||||
* the text of the point.
|
||||
* @param x
|
||||
* the x coordinate of the point.
|
||||
* @param y
|
||||
* the y coordinate of the point.
|
||||
* @param paintFront
|
||||
* the paintFront for the point.
|
||||
* @param paintBack
|
||||
* the paintBack for the point.
|
||||
*/
|
||||
PointTextContainer(String text, float x, float y, Paint paintFront, Paint paintBack) {
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.paintFront = paintFront;
|
||||
this.paintBack = paintBack;
|
||||
this.symbol = null;
|
||||
|
||||
this.boundary = new Rect();
|
||||
if (paintBack != null) {
|
||||
paintBack.getTextBounds(text, 0, text.length(), this.boundary);
|
||||
} else {
|
||||
paintFront.getTextBounds(text, 0, text.length(), this.boundary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new point container, that holds the x-y coordinates of a point, a text variable, two paint objects, and
|
||||
* a reference on a symbol, if the text is connected with a POI.
|
||||
*
|
||||
* @param text
|
||||
* the text of the point.
|
||||
* @param x
|
||||
* the x coordinate of the point.
|
||||
* @param y
|
||||
* the y coordinate of the point.
|
||||
* @param paintFront
|
||||
* the paintFront for the point.
|
||||
* @param paintBack
|
||||
* the paintBack for the point.
|
||||
* @param symbol
|
||||
* the connected Symbol.
|
||||
*/
|
||||
PointTextContainer(String text, float x, float y, Paint paintFront, Paint paintBack, SymbolContainer symbol) {
|
||||
this.text = text;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.paintFront = paintFront;
|
||||
this.paintBack = paintBack;
|
||||
this.symbol = symbol;
|
||||
|
||||
this.boundary = new Rect();
|
||||
if (paintBack != null) {
|
||||
paintBack.getTextBounds(text, 0, text.length(), this.boundary);
|
||||
} else {
|
||||
paintFront.getTextBounds(text, 0, text.length(), this.boundary);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/org/mapsforge/android/swrenderer/ShapeContainer.java
Normal file
19
src/org/mapsforge/android/swrenderer/ShapeContainer.java
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
interface ShapeContainer {
|
||||
ShapeType getShapeType();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
|
||||
import android.graphics.Paint;
|
||||
|
||||
class ShapePaintContainer {
|
||||
public Paint paint;
|
||||
public ShapeContainer shapeContainer;
|
||||
public ShapePaintContainer next;
|
||||
|
||||
ShapePaintContainer(ShapeContainer shapeContainer, Paint paint) {
|
||||
this.shapeContainer = shapeContainer;
|
||||
this.paint = paint;
|
||||
}
|
||||
}
|
||||
19
src/org/mapsforge/android/swrenderer/ShapeType.java
Normal file
19
src/org/mapsforge/android/swrenderer/ShapeType.java
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
enum ShapeType {
|
||||
CIRCLE, WAY;
|
||||
}
|
||||
61
src/org/mapsforge/android/swrenderer/SymbolContainer.java
Normal file
61
src/org/mapsforge/android/swrenderer/SymbolContainer.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
class SymbolContainer {
|
||||
final boolean alignCenter;
|
||||
final float rotation;
|
||||
final Bitmap symbol;
|
||||
final float x;
|
||||
final float y;
|
||||
|
||||
/**
|
||||
* Creates a new symbol container. The symbol will not be centered.
|
||||
*
|
||||
* @param symbol
|
||||
* the symbol to render at the point
|
||||
* @param x
|
||||
* the x coordinate of the point.
|
||||
* @param y
|
||||
* the y coordinate of the point.
|
||||
*/
|
||||
SymbolContainer(Bitmap symbol, float x, float y) {
|
||||
this(symbol, x, y, false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new symbol container.
|
||||
*
|
||||
* @param symbol
|
||||
* the symbol to render at the point
|
||||
* @param x
|
||||
* the x coordinate of the point.
|
||||
* @param y
|
||||
* the y coordinate of the point.
|
||||
* @param alignCenter
|
||||
* true if the symbol should be centered, false otherwise.
|
||||
* @param rotation
|
||||
* the rotation of the symbol.
|
||||
*/
|
||||
SymbolContainer(Bitmap symbol, float x, float y, boolean alignCenter, float rotation) {
|
||||
this.symbol = symbol;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.alignCenter = alignCenter;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
}
|
||||
34
src/org/mapsforge/android/swrenderer/WayDataContainer.java
Normal file
34
src/org/mapsforge/android/swrenderer/WayDataContainer.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
class WayDataContainer implements ShapeContainer {
|
||||
// position and length of float coordinates
|
||||
|
||||
int[] position;
|
||||
int[] length;
|
||||
|
||||
int[] textPos;
|
||||
|
||||
WayDataContainer(int size) {
|
||||
length = new int[size];
|
||||
position = new int[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShapeType getShapeType() {
|
||||
return ShapeType.WAY;
|
||||
}
|
||||
}
|
||||
292
src/org/mapsforge/android/swrenderer/WayDecorator.java
Normal file
292
src/org/mapsforge/android/swrenderer/WayDecorator.java
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mapsforge.android.utils.GeometryUtils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
|
||||
final class WayDecorator {
|
||||
/**
|
||||
* Minimum distance in pixels before the symbol is repeated.
|
||||
*/
|
||||
private static final int DISTANCE_BETWEEN_SYMBOLS = 200;
|
||||
|
||||
/**
|
||||
* Minimum distance in pixels before the way name is repeated.
|
||||
*/
|
||||
private static final int DISTANCE_BETWEEN_WAY_NAMES = 500;
|
||||
|
||||
/**
|
||||
* Distance in pixels to skip from both ends of a segment.
|
||||
*/
|
||||
private static final int SEGMENT_SAFETY_DISTANCE = 30;
|
||||
|
||||
static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter, boolean repeatSymbol, float[][] coordinates,
|
||||
List<SymbolContainer> waySymbols) {
|
||||
int skipPixels = SEGMENT_SAFETY_DISTANCE;
|
||||
|
||||
// get the first way point coordinates
|
||||
float previousX = coordinates[0][0];
|
||||
float previousY = coordinates[0][1];
|
||||
|
||||
// draw the symbol on each way segment
|
||||
float segmentLengthRemaining;
|
||||
float segmentSkipPercentage;
|
||||
float symbolAngle;
|
||||
for (int i = 2; i < coordinates[0].length; i += 2) {
|
||||
// get the current way point coordinates
|
||||
float currentX = coordinates[0][i];
|
||||
float currentY = coordinates[0][i + 1];
|
||||
|
||||
// calculate the length of the current segment (Euclidian distance)
|
||||
float diffX = currentX - previousX;
|
||||
float diffY = currentY - previousY;
|
||||
double segmentLengthInPixel = Math.sqrt(diffX * diffX + diffY * diffY);
|
||||
segmentLengthRemaining = (float) segmentLengthInPixel;
|
||||
|
||||
while (segmentLengthRemaining - skipPixels > SEGMENT_SAFETY_DISTANCE) {
|
||||
// calculate the percentage of the current segment to skip
|
||||
segmentSkipPercentage = skipPixels / segmentLengthRemaining;
|
||||
|
||||
// move the previous point forward towards the current point
|
||||
previousX += diffX * segmentSkipPercentage;
|
||||
previousY += diffY * segmentSkipPercentage;
|
||||
symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY, currentX - previousX));
|
||||
|
||||
waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY, alignCenter, symbolAngle));
|
||||
|
||||
// check if the symbol should only be rendered once
|
||||
if (!repeatSymbol) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recalculate the distances
|
||||
diffX = currentX - previousX;
|
||||
diffY = currentY - previousY;
|
||||
|
||||
// recalculate the remaining length of the current segment
|
||||
segmentLengthRemaining -= skipPixels;
|
||||
|
||||
// set the amount of pixels to skip before repeating the symbol
|
||||
skipPixels = DISTANCE_BETWEEN_SYMBOLS;
|
||||
}
|
||||
|
||||
skipPixels -= segmentLengthRemaining;
|
||||
if (skipPixels < SEGMENT_SAFETY_DISTANCE) {
|
||||
skipPixels = SEGMENT_SAFETY_DISTANCE;
|
||||
}
|
||||
|
||||
// set the previous way point coordinates for the next loop
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
}
|
||||
}
|
||||
|
||||
static void renderText(DatabaseRenderer databaseRenderer, Paint paint, Paint outline,
|
||||
float[] coordinates, WayDataContainer wayDataContainer,
|
||||
List<WayTextContainer> wayNames) {
|
||||
|
||||
int pos = wayDataContainer.position[0];
|
||||
int len = wayDataContainer.length[0];
|
||||
// int coordinatesLength
|
||||
|
||||
String text = null;
|
||||
// calculate the way name length plus some margin of safety
|
||||
float wayNameWidth = -1; // paint.measureText(textKey) + 5;
|
||||
float minWidth = 100;
|
||||
int skipPixels = 0;
|
||||
|
||||
// get the first way point coordinates
|
||||
int previousX = (int) coordinates[pos + 0];
|
||||
int previousY = (int) coordinates[pos + 1];
|
||||
int containerSize = -1;
|
||||
|
||||
// find way segments long enough to draw the way name on them
|
||||
for (int i = pos + 2; i < pos + len; i += 2) {
|
||||
// get the current way point coordinates
|
||||
int currentX = (int) coordinates[i];
|
||||
int currentY = (int) coordinates[i + 1];
|
||||
int first = i - 2;
|
||||
int last = i;
|
||||
|
||||
// calculate the length of the current segment (Euclidian distance)
|
||||
int diffX = currentX - previousX;
|
||||
int diffY = currentY - previousY;
|
||||
|
||||
for (int j = i + 2; j < pos + len; j += 2) {
|
||||
int nextX = (int) coordinates[j];
|
||||
int nextY = (int) coordinates[j + 1];
|
||||
|
||||
if (diffY == 0) {
|
||||
if ((currentY - nextY) != 0)
|
||||
break;
|
||||
|
||||
currentX = nextX;
|
||||
currentY = nextY;
|
||||
last = j;
|
||||
continue;
|
||||
} else if ((currentY - nextY) == 0)
|
||||
break;
|
||||
|
||||
float diff = ((float) (diffX) / (diffY) - (float) (currentX - nextX) / (currentY - nextY));
|
||||
|
||||
// skip segments with corners
|
||||
if (diff >= 0.2 || diff <= -0.2)
|
||||
break;
|
||||
|
||||
currentX = nextX;
|
||||
currentY = nextY;
|
||||
last = j;
|
||||
}
|
||||
|
||||
diffX = currentX - previousX;
|
||||
diffY = currentY - previousY;
|
||||
|
||||
if (diffX < 0)
|
||||
diffX = -diffX;
|
||||
if (diffY < 0)
|
||||
diffY = -diffY;
|
||||
|
||||
if (diffX + diffY < minWidth) {
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wayNameWidth > 0 && diffX + diffY < wayNameWidth) {
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
continue;
|
||||
}
|
||||
|
||||
double segmentLengthInPixel = Math.sqrt(diffX * diffX + diffY * diffY);
|
||||
|
||||
if (skipPixels > 0) {
|
||||
skipPixels -= segmentLengthInPixel;
|
||||
|
||||
} else if (segmentLengthInPixel > minWidth) {
|
||||
|
||||
if (wayNameWidth < 0) {
|
||||
if (text == null) {
|
||||
text = databaseRenderer.getWayName();
|
||||
if (text == null)
|
||||
text = "blub";
|
||||
}
|
||||
|
||||
wayNameWidth = (paint.measureText(text) + 10);
|
||||
}
|
||||
if (segmentLengthInPixel > wayNameWidth) {
|
||||
|
||||
double s = (wayNameWidth + 10) / segmentLengthInPixel;
|
||||
int width, height;
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
if (previousX < currentX) {
|
||||
x1 = previousX;
|
||||
y1 = previousY;
|
||||
x2 = currentX;
|
||||
y2 = currentY;
|
||||
} else {
|
||||
x1 = currentX;
|
||||
y1 = currentY;
|
||||
x2 = previousX;
|
||||
y2 = previousY;
|
||||
}
|
||||
|
||||
// estimate position of test on path
|
||||
width = (x2 - x1) / 2;
|
||||
x2 = x2 - (int) (width - s * width);
|
||||
x1 = x1 + (int) (width - s * width);
|
||||
|
||||
height = (y2 - y1) / 2;
|
||||
y2 = y2 - (int) (height - s * height);
|
||||
y1 = y1 + (int) (height - s * height);
|
||||
|
||||
short top = (short) (y1 < y2 ? y1 : y2);
|
||||
short bot = (short) (y1 < y2 ? y2 : y1);
|
||||
|
||||
boolean intersects = false;
|
||||
|
||||
if (containerSize == -1)
|
||||
containerSize = wayNames.size();
|
||||
|
||||
for (int k = 0; k < containerSize; k++) {
|
||||
WayTextContainer wtc2 = wayNames.get(k);
|
||||
if (!wtc2.match)
|
||||
// outline (not 'match') are appended to the end;
|
||||
break;
|
||||
|
||||
// check crossings
|
||||
if (GeometryUtils.lineIntersect(x1, y1, x2, y2, wtc2.x1, wtc2.y1, wtc2.x2, wtc2.y2)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// check overlapping labels of road with more than one
|
||||
// way
|
||||
short top2 = (wtc2.y1 < wtc2.y2 ? wtc2.y1 : wtc2.y2);
|
||||
short bot2 = (wtc2.y1 < wtc2.y2 ? wtc2.y2 : wtc2.y1);
|
||||
|
||||
if (x1 - 10 < wtc2.x2 && wtc2.x1 - 10 < x2 && top - 10 < bot2 && top2 - 10 < bot) {
|
||||
|
||||
if (wtc2.text.equals(text)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (intersects) {
|
||||
previousX = (int) coordinates[pos + i];
|
||||
previousY = (int) coordinates[pos + i + 1];
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.d("mapsforge", "add " + text + " " + first + " " + last);
|
||||
WayTextContainer wtc = new WayTextContainer(first, last, wayDataContainer, text,
|
||||
paint);
|
||||
wtc.x1 = (short) x1;
|
||||
wtc.y1 = (short) y1;
|
||||
wtc.x2 = (short) x2;
|
||||
wtc.y2 = (short) y2;
|
||||
wtc.match = true;
|
||||
|
||||
wayNames.add(0, wtc);
|
||||
containerSize++;
|
||||
|
||||
if (outline != null) {
|
||||
wayNames.add(new WayTextContainer(first, last, wayDataContainer, text, outline));
|
||||
containerSize++;
|
||||
}
|
||||
// 500 ??? how big is a tile?!
|
||||
skipPixels = DISTANCE_BETWEEN_WAY_NAMES;
|
||||
}
|
||||
}
|
||||
|
||||
// store the previous way point coordinates
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
}
|
||||
}
|
||||
|
||||
private WayDecorator() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
37
src/org/mapsforge/android/swrenderer/WayTextContainer.java
Normal file
37
src/org/mapsforge/android/swrenderer/WayTextContainer.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.mapsforge.android.swrenderer;
|
||||
|
||||
|
||||
import android.graphics.Paint;
|
||||
|
||||
class WayTextContainer {
|
||||
final WayDataContainer wayDataContainer;
|
||||
final int first;
|
||||
final int last;
|
||||
final Paint paint;
|
||||
final String text;
|
||||
short x1, y1, x2, y2;
|
||||
boolean match;
|
||||
|
||||
WayTextContainer(int first, int last, WayDataContainer wayDataContainer, String text, Paint paint) {
|
||||
this.wayDataContainer = wayDataContainer;
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
this.text = text;
|
||||
this.paint = paint;
|
||||
this.match = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user