switch package org.mapsforge -> org.oscim
This commit is contained in:
66
src/org/oscim/view/Compass.java
Normal file
66
src/org/oscim/view/Compass.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
|
||||
public class Compass {
|
||||
|
||||
private final SensorEventListener mListener = new SensorEventListener() {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (Math.abs(event.values[0] - mAngle) > 0.25) {
|
||||
mAngle = event.values[0];
|
||||
|
||||
if (mMapView != null) {
|
||||
mMapView.getMapPosition().setRotation(mAngle);
|
||||
mMapView.redrawTiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
}
|
||||
};
|
||||
|
||||
/* package */float mAngle = 0;
|
||||
/* package */MapView mMapView;
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mSensor;
|
||||
|
||||
public Compass(MapActivity mapActivity, MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mSensorManager = (SensorManager) mapActivity
|
||||
.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||
}
|
||||
|
||||
void enable() {
|
||||
mSensorManager.registerListener(mListener, mSensor,
|
||||
SensorManager.SENSOR_DELAY_GAME);
|
||||
}
|
||||
|
||||
void disable() {
|
||||
mSensorManager.unregisterListener(mListener);
|
||||
mMapView.getMapPosition().setRotation(0);
|
||||
}
|
||||
}
|
||||
56
src/org/oscim/view/DebugSettings.java
Normal file
56
src/org/oscim/view/DebugSettings.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
/**
|
||||
* A simple DTO to stores flags for debugging rendered map tiles.
|
||||
*/
|
||||
public class DebugSettings {
|
||||
|
||||
/**
|
||||
* True if drawing of tile coordinates is enabled, false otherwise.
|
||||
*/
|
||||
public final boolean mDrawTileCoordinates;
|
||||
|
||||
/**
|
||||
* True if drawing of tile frames is enabled, false otherwise.
|
||||
*/
|
||||
public final boolean mDrawTileFrames;
|
||||
|
||||
public final boolean mDrawUnmatchted;
|
||||
|
||||
/**
|
||||
* True if highlighting of water tiles is enabled, false otherwise.
|
||||
*/
|
||||
public final boolean mDisablePolygons;
|
||||
|
||||
/**
|
||||
* @param drawTileCoordinates
|
||||
* if drawing of tile coordinates is enabled.
|
||||
* @param drawTileFrames
|
||||
* if drawing of tile frames is enabled.
|
||||
* @param disablePolygons
|
||||
* if highlighting of water tiles is enabled.
|
||||
* @param drawUnmatched
|
||||
* ...
|
||||
*/
|
||||
public DebugSettings(boolean drawTileCoordinates, boolean drawTileFrames,
|
||||
boolean disablePolygons, boolean drawUnmatched) {
|
||||
mDrawTileCoordinates = drawTileCoordinates;
|
||||
mDrawTileFrames = drawTileFrames;
|
||||
mDrawUnmatchted = drawUnmatched;
|
||||
mDisablePolygons = disablePolygons;
|
||||
}
|
||||
}
|
||||
54
src/org/oscim/view/IMapRenderer.java
Normal file
54
src/org/oscim/view/IMapRenderer.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view;
|
||||
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
|
||||
import android.opengl.GLSurfaceView;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface IMapRenderer extends GLSurfaceView.Renderer {
|
||||
|
||||
/**
|
||||
* @param tile
|
||||
* Tile data ready for rendering
|
||||
* @return true
|
||||
*/
|
||||
public boolean passTile(JobTile tile);
|
||||
|
||||
/**
|
||||
* called by MapView on position and map changes
|
||||
*
|
||||
* @param clear
|
||||
* recreate all tiles (i.e. on theme change)
|
||||
*/
|
||||
public void updateMap(boolean clear);
|
||||
|
||||
/**
|
||||
* @return new MapGenerator Instance for this Renderer
|
||||
*/
|
||||
public IMapGenerator createMapGenerator();
|
||||
|
||||
/**
|
||||
* @param t
|
||||
* the theme
|
||||
*/
|
||||
public void setRenderTheme(RenderTheme t);
|
||||
|
||||
}
|
||||
135
src/org/oscim/view/MapActivity.java
Normal file
135
src/org/oscim/view/MapActivity.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.theme.InternalRenderTheme;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
/**
|
||||
* MapActivity is the abstract base class which must be extended in order to use a {@link MapView}. There are no
|
||||
* abstract methods in this implementation which subclasses need to override and no API key or registration is required.
|
||||
* <p>
|
||||
* A subclass may create a MapView either via one of the MapView constructors or by inflating an XML layout file. It is
|
||||
* possible to use more than one MapView at the same time.
|
||||
* <p>
|
||||
* When the MapActivity is shut down, the current center position, zoom level and map file of the MapView are saved in a
|
||||
* preferences file and restored in the next startup process.
|
||||
*/
|
||||
public abstract class MapActivity extends Activity {
|
||||
private static final String KEY_LATITUDE = "latitude";
|
||||
private static final String KEY_LONGITUDE = "longitude";
|
||||
private static final String KEY_MAP_FILE = "mapFile";
|
||||
private static final String KEY_ZOOM_LEVEL = "zoomLevel";
|
||||
private static final String PREFERENCES_FILE = "MapActivity";
|
||||
private static final String KEY_THEME = "Theme";
|
||||
|
||||
private static boolean containsMapViewPosition(SharedPreferences sharedPreferences) {
|
||||
return sharedPreferences.contains(KEY_LATITUDE)
|
||||
&& sharedPreferences.contains(KEY_LONGITUDE)
|
||||
&& sharedPreferences.contains(KEY_ZOOM_LEVEL);
|
||||
}
|
||||
|
||||
private MapView mMapView;
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mMapView.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mMapView.onPause();
|
||||
|
||||
Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit();
|
||||
editor.clear();
|
||||
|
||||
// save the map position and zoom level
|
||||
MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
if (mapPosition != null) {
|
||||
GeoPoint geoPoint = new GeoPoint(mapPosition.lat, mapPosition.lon);
|
||||
editor.putInt(KEY_LATITUDE, geoPoint.latitudeE6);
|
||||
editor.putInt(KEY_LONGITUDE, geoPoint.longitudeE6);
|
||||
editor.putInt(KEY_ZOOM_LEVEL, mapPosition.zoomLevel);
|
||||
}
|
||||
|
||||
// if (mMapView.getMapFile() != null) {
|
||||
// // save the map file
|
||||
// editor.putString(KEY_MAP_FILE, mMapView.getMapFile());
|
||||
// }
|
||||
|
||||
editor.putString(KEY_THEME, mMapView.getRenderTheme());
|
||||
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mMapView.onResume();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called once by each MapView during its setup process.
|
||||
*
|
||||
* @param mapView
|
||||
* the calling MapView.
|
||||
*/
|
||||
final void registerMapView(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES_FILE,
|
||||
MODE_PRIVATE);
|
||||
|
||||
if (containsMapViewPosition(sharedPreferences)) {
|
||||
//
|
||||
// if (sharedPreferences.contains(KEY_MAP_FILE)) {
|
||||
// // get and set the map file
|
||||
// mapView.setMapFile(sharedPreferences.getString(KEY_MAP_FILE, null));
|
||||
// }
|
||||
|
||||
// get and set the map position and zoom level
|
||||
int latitudeE6 = sharedPreferences.getInt(KEY_LATITUDE, 0);
|
||||
int longitudeE6 = sharedPreferences.getInt(KEY_LONGITUDE, 0);
|
||||
int zoomLevel = sharedPreferences.getInt(KEY_ZOOM_LEVEL, -1);
|
||||
|
||||
GeoPoint geoPoint = new GeoPoint(latitudeE6, longitudeE6);
|
||||
MapPosition mapPosition = new MapPosition(geoPoint, (byte) zoomLevel, 1);
|
||||
|
||||
mapView.setMapCenter(mapPosition);
|
||||
}
|
||||
|
||||
String theme = sharedPreferences.getString(KEY_THEME,
|
||||
InternalRenderTheme.OSMARENDER.name());
|
||||
|
||||
if (theme.startsWith("/")) {
|
||||
try {
|
||||
mapView.setRenderTheme(theme);
|
||||
} catch (FileNotFoundException e) {
|
||||
mapView.setRenderTheme(InternalRenderTheme.OSMARENDER);
|
||||
}
|
||||
} else {
|
||||
mapView.setRenderTheme(InternalRenderTheme.valueOf(theme));
|
||||
}
|
||||
}
|
||||
}
|
||||
76
src/org/oscim/view/MapRendererFactory.java
Normal file
76
src/org/oscim/view/MapRendererFactory.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import android.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* A factory for the internal MapRenderer implementations.
|
||||
*/
|
||||
public final class MapRendererFactory {
|
||||
private static final String MAP_RENDERER_ATTRIBUTE_NAME = "mapRenderer";
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* ...
|
||||
* @param attributeSet
|
||||
* A collection of attributes which includes the desired MapRenderer.
|
||||
* @return a new MapRenderer instance.
|
||||
*/
|
||||
public static IMapRenderer createMapRenderer(MapView mapView,
|
||||
AttributeSet attributeSet) {
|
||||
String mapRendererName = attributeSet.getAttributeValue(null,
|
||||
MAP_RENDERER_ATTRIBUTE_NAME);
|
||||
if (mapRendererName == null) {
|
||||
return new org.oscim.view.glrenderer.MapRenderer(mapView);
|
||||
}
|
||||
|
||||
MapRenderers mapRendererInternal = MapRenderers.valueOf(mapRendererName);
|
||||
return MapRendererFactory.createMapRenderer(mapView, mapRendererInternal);
|
||||
}
|
||||
|
||||
public static MapRenderers getMapRenderer(AttributeSet attributeSet) {
|
||||
String mapRendererName = attributeSet.getAttributeValue(null,
|
||||
MAP_RENDERER_ATTRIBUTE_NAME);
|
||||
if (mapRendererName == null) {
|
||||
return MapRenderers.GL_RENDERER;
|
||||
}
|
||||
|
||||
return MapRenderers.valueOf(mapRendererName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* ...
|
||||
* @param mapRendererInternal
|
||||
* the internal MapRenderer implementation.
|
||||
* @return a new MapRenderer instance.
|
||||
*/
|
||||
public static IMapRenderer createMapRenderer(MapView mapView,
|
||||
MapRenderers mapRendererInternal) {
|
||||
switch (mapRendererInternal) {
|
||||
case SW_RENDERER:
|
||||
return new org.oscim.view.swrenderer.MapRenderer(mapView);
|
||||
case GL_RENDERER:
|
||||
return new org.oscim.view.glrenderer.MapRenderer(mapView);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("unknown enum value: " + mapRendererInternal);
|
||||
}
|
||||
|
||||
private MapRendererFactory() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
30
src/org/oscim/view/MapRenderers.java
Normal file
30
src/org/oscim/view/MapRenderers.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
/**
|
||||
* Enumeration of all internal MapGenerator implementations.
|
||||
*/
|
||||
public enum MapRenderers {
|
||||
/**
|
||||
* texture renderer.
|
||||
*/
|
||||
SW_RENDERER,
|
||||
|
||||
/**
|
||||
* opengl renderer.
|
||||
*/
|
||||
GL_RENDERER
|
||||
}
|
||||
278
src/org/oscim/view/MapScaleBar.java
Normal file
278
src/org/oscim/view/MapScaleBar.java
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
|
||||
/**
|
||||
* A MapScaleBar displays the ratio of a distance on the map to the corresponding distance on the ground.
|
||||
*/
|
||||
public class MapScaleBar {
|
||||
/**
|
||||
* Enumeration of all text fields.
|
||||
*/
|
||||
public enum TextField {
|
||||
/**
|
||||
* Unit symbol for one foot.
|
||||
*/
|
||||
FOOT,
|
||||
|
||||
/**
|
||||
* Unit symbol for one kilometer.
|
||||
*/
|
||||
KILOMETER,
|
||||
|
||||
/**
|
||||
* Unit symbol for one meter.
|
||||
*/
|
||||
METER,
|
||||
|
||||
/**
|
||||
* Unit symbol for one mile.
|
||||
*/
|
||||
MILE;
|
||||
}
|
||||
|
||||
private static final int BITMAP_HEIGHT = 50;
|
||||
private static final int BITMAP_WIDTH = 150;
|
||||
private static final double LATITUDE_REDRAW_THRESHOLD = 0.2;
|
||||
private static final int MARGIN_BOTTOM = 5;
|
||||
private static final int MARGIN_LEFT = 5;
|
||||
private static final double METER_FOOT_RATIO = 0.3048;
|
||||
private static final int ONE_KILOMETER = 1000;
|
||||
private static final int ONE_MILE = 5280;
|
||||
private static final Paint SCALE_BAR = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final Paint SCALE_BAR_STROKE = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final int[] SCALE_BAR_VALUES_IMPERIAL = { 26400000, 10560000, 5280000,
|
||||
2640000, 1056000, 528000,
|
||||
264000, 105600, 52800, 26400, 10560, 5280, 2000, 1000, 500, 200, 100, 50, 20,
|
||||
10, 5, 2, 1 };
|
||||
private static final int[] SCALE_BAR_VALUES_METRIC = { 10000000, 5000000, 2000000,
|
||||
1000000, 500000, 200000, 100000,
|
||||
50000, 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1 };
|
||||
private static final Paint SCALE_TEXT = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final Paint SCALE_TEXT_STROKE = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
private static void configurePaints() {
|
||||
SCALE_BAR.setStrokeWidth(2);
|
||||
SCALE_BAR.setStrokeCap(Paint.Cap.SQUARE);
|
||||
SCALE_BAR.setColor(Color.BLACK);
|
||||
SCALE_BAR_STROKE.setStrokeWidth(5);
|
||||
SCALE_BAR_STROKE.setStrokeCap(Paint.Cap.SQUARE);
|
||||
SCALE_BAR_STROKE.setColor(Color.WHITE);
|
||||
|
||||
SCALE_TEXT.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
SCALE_TEXT.setTextSize(17);
|
||||
SCALE_TEXT.setColor(Color.BLACK);
|
||||
SCALE_TEXT_STROKE.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
SCALE_TEXT_STROKE.setStyle(Paint.Style.STROKE);
|
||||
SCALE_TEXT_STROKE.setColor(Color.WHITE);
|
||||
SCALE_TEXT_STROKE.setStrokeWidth(2);
|
||||
SCALE_TEXT_STROKE.setTextSize(17);
|
||||
}
|
||||
|
||||
private boolean mImperialUnits;
|
||||
private MapPosition mMapPosition;
|
||||
private final Bitmap mMapScaleBitmap;
|
||||
private final Canvas mMapScaleCanvas;
|
||||
private final MapView mMapView;
|
||||
private boolean mRedrawNeeded;
|
||||
private boolean mShowMapScaleBar;
|
||||
private final Map<TextField, String> mTextFields;
|
||||
|
||||
MapScaleBar(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mMapScaleBitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT,
|
||||
Bitmap.Config.ARGB_4444);
|
||||
mMapScaleCanvas = new Canvas(mMapScaleBitmap);
|
||||
mTextFields = new HashMap<TextField, String>();
|
||||
setDefaultTexts();
|
||||
configurePaints();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if imperial units are used, false otherwise.
|
||||
*/
|
||||
public boolean isImperialUnits() {
|
||||
return mImperialUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this map scale bar is visible, false otherwise.
|
||||
*/
|
||||
public boolean isShowMapScaleBar() {
|
||||
return mShowMapScaleBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param imperialUnits
|
||||
* true if imperial units should be used rather than metric units.
|
||||
*/
|
||||
public void setImperialUnits(boolean imperialUnits) {
|
||||
mImperialUnits = imperialUnits;
|
||||
mRedrawNeeded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param showMapScaleBar
|
||||
* true if the map scale bar should be drawn, false otherwise.
|
||||
*/
|
||||
public void setShowMapScaleBar(boolean showMapScaleBar) {
|
||||
mShowMapScaleBar = showMapScaleBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the specified text field with the given string.
|
||||
*
|
||||
* @param textField
|
||||
* the text field to override.
|
||||
* @param value
|
||||
* the new value of the text field.
|
||||
*/
|
||||
public void setText(TextField textField, String value) {
|
||||
mTextFields.put(textField, value);
|
||||
mRedrawNeeded = true;
|
||||
}
|
||||
|
||||
private void drawScaleBar(float scaleBarLength, Paint paint) {
|
||||
mMapScaleCanvas.drawLine(7, 25, scaleBarLength + 3, 25, paint);
|
||||
mMapScaleCanvas.drawLine(5, 10, 5, 40, paint);
|
||||
mMapScaleCanvas.drawLine(scaleBarLength + 5, 10, scaleBarLength + 5, 40, paint);
|
||||
}
|
||||
|
||||
private void drawScaleText(int scaleValue, String unitSymbol, Paint paint) {
|
||||
mMapScaleCanvas.drawText(scaleValue + unitSymbol, 12, 18, paint);
|
||||
}
|
||||
|
||||
private boolean isRedrawNecessary() {
|
||||
if (mRedrawNeeded || mMapPosition == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MapPosition currentMapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
|
||||
if (currentMapPosition.zoomLevel != mMapPosition.zoomLevel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
double latitudeDiff = Math.abs(currentMapPosition.lat
|
||||
- mMapPosition.lat);
|
||||
if (latitudeDiff > LATITUDE_REDRAW_THRESHOLD) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraws the map scale bitmap with the given parameters.
|
||||
*
|
||||
* @param scaleBarLength
|
||||
* the length of the map scale bar in pixels.
|
||||
* @param mapScaleValue
|
||||
* the map scale value in meters.
|
||||
*/
|
||||
private void redrawMapScaleBitmap(float scaleBarLength, int mapScaleValue) {
|
||||
mMapScaleBitmap.eraseColor(Color.TRANSPARENT);
|
||||
|
||||
// draw the scale bar
|
||||
drawScaleBar(scaleBarLength, SCALE_BAR_STROKE);
|
||||
drawScaleBar(scaleBarLength, SCALE_BAR);
|
||||
|
||||
int scaleValue;
|
||||
String unitSymbol;
|
||||
if (mImperialUnits) {
|
||||
if (mapScaleValue < ONE_MILE) {
|
||||
scaleValue = mapScaleValue;
|
||||
unitSymbol = mTextFields.get(TextField.FOOT);
|
||||
} else {
|
||||
scaleValue = mapScaleValue / ONE_MILE;
|
||||
unitSymbol = mTextFields.get(TextField.MILE);
|
||||
}
|
||||
} else {
|
||||
if (mapScaleValue < ONE_KILOMETER) {
|
||||
scaleValue = mapScaleValue;
|
||||
unitSymbol = mTextFields.get(TextField.METER);
|
||||
} else {
|
||||
scaleValue = mapScaleValue / ONE_KILOMETER;
|
||||
unitSymbol = mTextFields.get(TextField.KILOMETER);
|
||||
}
|
||||
}
|
||||
|
||||
// draw the scale text
|
||||
drawScaleText(scaleValue, unitSymbol, SCALE_TEXT_STROKE);
|
||||
drawScaleText(scaleValue, unitSymbol, SCALE_TEXT);
|
||||
}
|
||||
|
||||
private void setDefaultTexts() {
|
||||
mTextFields.put(TextField.FOOT, " ft");
|
||||
mTextFields.put(TextField.MILE, " mi");
|
||||
|
||||
mTextFields.put(TextField.METER, " m");
|
||||
mTextFields.put(TextField.KILOMETER, " km");
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
mMapScaleBitmap.recycle();
|
||||
}
|
||||
|
||||
void draw(Canvas canvas) {
|
||||
int top = mMapView.getHeight() - BITMAP_HEIGHT - MARGIN_BOTTOM;
|
||||
canvas.drawBitmap(mMapScaleBitmap, MARGIN_LEFT, top, null);
|
||||
}
|
||||
|
||||
void redrawScaleBar() {
|
||||
if (!isRedrawNecessary()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
double groundResolution = MercatorProjection.calculateGroundResolution(
|
||||
mMapPosition.lat,
|
||||
mMapPosition.zoomLevel);
|
||||
|
||||
int[] scaleBarValues;
|
||||
if (mImperialUnits) {
|
||||
groundResolution = groundResolution / METER_FOOT_RATIO;
|
||||
scaleBarValues = SCALE_BAR_VALUES_IMPERIAL;
|
||||
} else {
|
||||
scaleBarValues = SCALE_BAR_VALUES_METRIC;
|
||||
}
|
||||
|
||||
float scaleBarLength = 0;
|
||||
int mapScaleValue = 0;
|
||||
|
||||
for (int i = 0; i < scaleBarValues.length; ++i) {
|
||||
mapScaleValue = scaleBarValues[i];
|
||||
scaleBarLength = mapScaleValue / (float) groundResolution;
|
||||
if (scaleBarLength < (BITMAP_WIDTH - 10)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
redrawMapScaleBitmap(scaleBarLength, mapScaleValue);
|
||||
mRedrawNeeded = false;
|
||||
}
|
||||
}
|
||||
676
src/org/oscim/view/MapView.java
Normal file
676
src/org/oscim/view/MapView.java
Normal file
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.database.IMapDatabase;
|
||||
import org.oscim.database.MapDatabaseFactory;
|
||||
import org.oscim.database.MapDatabases;
|
||||
import org.oscim.database.MapInfo;
|
||||
import org.oscim.database.OpenResult;
|
||||
import org.oscim.theme.ExternalRenderTheme;
|
||||
import org.oscim.theme.InternalRenderTheme;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.RenderThemeHandler;
|
||||
import org.oscim.theme.Theme;
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
import org.oscim.view.mapgenerator.JobQueue;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
import org.oscim.view.mapgenerator.MapWorker;
|
||||
import org.oscim.view.utils.GlConfigChooser;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* A MapView shows a map on the display of the device. It handles all user input and touch gestures to move and zoom the
|
||||
* map.
|
||||
*/
|
||||
public class MapView extends GLSurfaceView {
|
||||
|
||||
final static String TAG = "MapView";
|
||||
|
||||
/**
|
||||
* Default render theme of the MapView.
|
||||
*/
|
||||
public static final InternalRenderTheme DEFAULT_RENDER_THEME = InternalRenderTheme.OSMARENDER;
|
||||
|
||||
// private static final float DEFAULT_TEXT_SCALE = 1;
|
||||
// private static final Byte DEFAULT_START_ZOOM_LEVEL = Byte.valueOf((byte) 16);
|
||||
|
||||
public final static boolean debugFrameTime = false;
|
||||
|
||||
public boolean enableRotation = false;
|
||||
public boolean enableCompass = false;
|
||||
|
||||
private final MapViewPosition mMapViewPosition;
|
||||
|
||||
private final MapZoomControls mMapZoomControls;
|
||||
private final Projection mProjection;
|
||||
private final TouchHandler mTouchEventHandler;
|
||||
private final Compass mCompass;
|
||||
|
||||
private IMapDatabase mMapDatabase;
|
||||
private MapDatabases mMapDatabaseType;
|
||||
private IMapRenderer mMapRenderer;
|
||||
private JobQueue mJobQueue;
|
||||
private MapWorker mMapWorkers[];
|
||||
private int mNumMapWorkers = 4;
|
||||
private DebugSettings debugSettings;
|
||||
private String mRenderTheme;
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* the enclosing MapActivity instance.
|
||||
* @throws IllegalArgumentException
|
||||
* if the context object is not an instance of {@link MapActivity} .
|
||||
*/
|
||||
public MapView(Context context) {
|
||||
this(context, null, MapRenderers.GL_RENDERER, MapDatabases.MAP_READER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* the enclosing MapActivity instance.
|
||||
* @param attributeSet
|
||||
* a set of attributes.
|
||||
* @throws IllegalArgumentException
|
||||
* if the context object is not an instance of {@link MapActivity} .
|
||||
*/
|
||||
public MapView(Context context, AttributeSet attributeSet) {
|
||||
this(context, attributeSet,
|
||||
MapRendererFactory.getMapRenderer(attributeSet),
|
||||
MapDatabaseFactory.getMapDatabase(attributeSet));
|
||||
}
|
||||
|
||||
private boolean mDebugDatabase = false;
|
||||
|
||||
private MapView(Context context, AttributeSet attributeSet,
|
||||
MapRenderers mapGeneratorType, MapDatabases mapDatabaseType) {
|
||||
|
||||
super(context, attributeSet);
|
||||
|
||||
if (!(context instanceof MapActivity)) {
|
||||
throw new IllegalArgumentException(
|
||||
"context is not an instance of MapActivity");
|
||||
}
|
||||
|
||||
Log.d(TAG, "create MapView: " + mapDatabaseType.name());
|
||||
|
||||
// TODO make this dpi dependent
|
||||
Tile.TILE_SIZE = 400;
|
||||
|
||||
MapActivity mapActivity = (MapActivity) context;
|
||||
|
||||
debugSettings = new DebugSettings(false, false, false, false);
|
||||
|
||||
mMapDatabaseType = mapDatabaseType;
|
||||
|
||||
mMapViewPosition = new MapViewPosition(this);
|
||||
|
||||
mMapZoomControls = new MapZoomControls(mapActivity, this);
|
||||
|
||||
mProjection = new MapViewProjection(this);
|
||||
|
||||
mTouchEventHandler = new TouchHandler(mapActivity, this);
|
||||
|
||||
mCompass = new Compass(mapActivity, this);
|
||||
|
||||
mJobQueue = new JobQueue();
|
||||
|
||||
mMapRenderer = MapRendererFactory.createMapRenderer(this, mapGeneratorType);
|
||||
mMapWorkers = new MapWorker[mNumMapWorkers];
|
||||
|
||||
for (int i = 0; i < mNumMapWorkers; i++) {
|
||||
IMapDatabase mapDatabase;
|
||||
if (mDebugDatabase) {
|
||||
mapDatabase = MapDatabaseFactory
|
||||
.createMapDatabase(MapDatabases.TEST_READER);
|
||||
} else {
|
||||
mapDatabase = MapDatabaseFactory.createMapDatabase(mapDatabaseType);
|
||||
}
|
||||
|
||||
IMapGenerator mapGenerator = mMapRenderer.createMapGenerator();
|
||||
mapGenerator.setMapDatabase(mapDatabase);
|
||||
|
||||
if (i == 0)
|
||||
mMapDatabase = mapDatabase;
|
||||
|
||||
mMapWorkers[i] = new MapWorker(i, this, mapGenerator, mMapRenderer);
|
||||
mMapWorkers[i].start();
|
||||
}
|
||||
|
||||
mapActivity.registerMapView(this);
|
||||
|
||||
if (!mMapDatabase.isOpen()) {
|
||||
Log.d(TAG, "open database with defaults");
|
||||
setMapOptions(null);
|
||||
}
|
||||
if (!mMapViewPosition.isValid()) {
|
||||
Log.d(TAG, "set default start position");
|
||||
setMapCenter(getStartPosition());
|
||||
}
|
||||
|
||||
setEGLConfigChooser(new GlConfigChooser());
|
||||
setEGLContextClientVersion(2);
|
||||
|
||||
// setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
|
||||
|
||||
setRenderer(mMapRenderer);
|
||||
|
||||
if (!debugFrameTime)
|
||||
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the debug settings which are used in this MapView.
|
||||
*/
|
||||
public DebugSettings getDebugSettings() {
|
||||
return debugSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the job queue which is used in this MapView.
|
||||
*/
|
||||
public JobQueue getJobQueue() {
|
||||
return mJobQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the map database which is used for reading map files.
|
||||
*/
|
||||
public IMapDatabase getMapDatabase() {
|
||||
return mMapDatabase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current position and zoom level of this MapView.
|
||||
*/
|
||||
public MapViewPosition getMapPosition() {
|
||||
return mMapViewPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the currently used projection of the map. Do not keep this object for a longer time.
|
||||
*/
|
||||
public Projection getProjection() {
|
||||
return mProjection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent motionEvent) {
|
||||
if (this.isClickable())
|
||||
return mTouchEventHandler.handleMotionEvent(motionEvent);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates all necessary tiles and adds jobs accordingly.
|
||||
*/
|
||||
public void redrawTiles() {
|
||||
mMapRenderer.updateMap(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param debugSettings
|
||||
* the new DebugSettings for this MapView.
|
||||
*/
|
||||
public void setDebugSettings(DebugSettings debugSettings) {
|
||||
this.debugSettings = debugSettings;
|
||||
mMapRenderer.updateMap(true);
|
||||
}
|
||||
|
||||
private Map<String, String> mMapOptions;
|
||||
|
||||
public Map<String, String> getMapOptions() {
|
||||
return mMapOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the map file for this MapView.
|
||||
*
|
||||
* @param mapOptions
|
||||
* ...
|
||||
* @return true if the map file was set correctly, false otherwise.
|
||||
*/
|
||||
public boolean setMapOptions(Map<String, String> mapOptions) {
|
||||
OpenResult openResult = null;
|
||||
|
||||
boolean initialized = false;
|
||||
|
||||
mJobQueue.clear();
|
||||
mapWorkersPause(true);
|
||||
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
|
||||
IMapGenerator mapGenerator = mapWorker.getMapGenerator();
|
||||
IMapDatabase mapDatabase = mapGenerator.getMapDatabase();
|
||||
|
||||
mapDatabase.close();
|
||||
openResult = mapDatabase.open(null);
|
||||
|
||||
if (openResult.isSuccess())
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
mapWorkersProceed();
|
||||
|
||||
if (initialized) {
|
||||
mMapOptions = mapOptions;
|
||||
mMapRenderer.updateMap(true);
|
||||
Log.i(TAG, "MapDatabase ready");
|
||||
return true;
|
||||
}
|
||||
|
||||
mMapOptions = null;
|
||||
Log.i(TAG, "Opening MapDatabase failed");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private MapPosition getStartPosition() {
|
||||
if (mMapDatabase == null)
|
||||
return new MapPosition();
|
||||
|
||||
MapInfo mapInfo = mMapDatabase.getMapInfo();
|
||||
if (mapInfo == null)
|
||||
return new MapPosition();
|
||||
|
||||
GeoPoint startPos = mapInfo.startPosition;
|
||||
|
||||
if (startPos == null)
|
||||
startPos = mapInfo.mapCenter;
|
||||
|
||||
if (startPos == null)
|
||||
startPos = new GeoPoint(0, 0);
|
||||
|
||||
if (mapInfo.startZoomLevel != null)
|
||||
return new MapPosition(startPos, (mapInfo.startZoomLevel).byteValue(), 1);
|
||||
|
||||
return new MapPosition(startPos, (byte) 1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MapDatabase for this MapView.
|
||||
*
|
||||
* @param mapDatabaseType
|
||||
* the new MapDatabase.
|
||||
*/
|
||||
|
||||
public void setMapDatabase(MapDatabases mapDatabaseType) {
|
||||
if (mDebugDatabase)
|
||||
return;
|
||||
|
||||
IMapGenerator mapGenerator;
|
||||
|
||||
Log.i(TAG, "setMapDatabase " + mapDatabaseType.name());
|
||||
|
||||
if (mMapDatabaseType == mapDatabaseType)
|
||||
return;
|
||||
|
||||
mMapDatabaseType = mapDatabaseType;
|
||||
|
||||
mapWorkersPause(true);
|
||||
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
mapGenerator = mapWorker.getMapGenerator();
|
||||
|
||||
mapGenerator.setMapDatabase(MapDatabaseFactory
|
||||
.createMapDatabase(mapDatabaseType));
|
||||
}
|
||||
|
||||
mJobQueue.clear();
|
||||
|
||||
// String mapFile = mMapFile;
|
||||
// mMapFile = null;
|
||||
setMapOptions(null);
|
||||
|
||||
mapWorkersProceed();
|
||||
}
|
||||
|
||||
public String getRenderTheme() {
|
||||
return mRenderTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal theme which is used for rendering the map.
|
||||
*
|
||||
* @param internalRenderTheme
|
||||
* the internal rendering theme.
|
||||
* @return ...
|
||||
* @throws IllegalArgumentException
|
||||
* if the supplied internalRenderTheme is null.
|
||||
*/
|
||||
public boolean setRenderTheme(InternalRenderTheme internalRenderTheme) {
|
||||
if (internalRenderTheme == null) {
|
||||
throw new IllegalArgumentException("render theme must not be null");
|
||||
}
|
||||
|
||||
boolean ret = setRenderTheme((Theme) internalRenderTheme);
|
||||
if (ret) {
|
||||
mRenderTheme = internalRenderTheme.name();
|
||||
}
|
||||
mMapRenderer.updateMap(true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme file which is used for rendering the map.
|
||||
*
|
||||
* @param renderThemePath
|
||||
* the path to the XML file which defines the rendering theme.
|
||||
* @throws IllegalArgumentException
|
||||
* if the supplied internalRenderTheme is null.
|
||||
* @throws FileNotFoundException
|
||||
* if the supplied file does not exist, is a directory or cannot be read.
|
||||
*/
|
||||
public void setRenderTheme(String renderThemePath) throws FileNotFoundException {
|
||||
if (renderThemePath == null) {
|
||||
throw new IllegalArgumentException("render theme path must not be null");
|
||||
}
|
||||
|
||||
boolean ret = setRenderTheme(new ExternalRenderTheme(renderThemePath));
|
||||
if (ret) {
|
||||
mRenderTheme = renderThemePath;
|
||||
}
|
||||
mMapRenderer.updateMap(true);
|
||||
}
|
||||
|
||||
private boolean setRenderTheme(Theme theme) {
|
||||
|
||||
mapWorkersPause(true);
|
||||
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = theme.getRenderThemeAsStream();
|
||||
RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream);
|
||||
mMapRenderer.setRenderTheme(t);
|
||||
mMapWorkers[0].getMapGenerator().setRenderTheme(t);
|
||||
return true;
|
||||
} catch (ParserConfigurationException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
} catch (SAXException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
mapWorkersProceed();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void onSizeChanged(int width, int height,
|
||||
int oldWidth, int oldHeight) {
|
||||
|
||||
mJobQueue.clear();
|
||||
mapWorkersPause(true);
|
||||
|
||||
super.onSizeChanged(width, height, oldWidth, oldHeight);
|
||||
|
||||
mapWorkersProceed();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
mapWorker.pause();
|
||||
mapWorker.interrupt();
|
||||
|
||||
try {
|
||||
mapWorker.join();
|
||||
} catch (InterruptedException e) {
|
||||
// restore the interrupted status
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
IMapDatabase mapDatabase = mapWorker.getMapGenerator().getMapDatabase();
|
||||
mapDatabase.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mapWorkersPause(false);
|
||||
|
||||
if (this.enableCompass)
|
||||
mCompass.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mapWorkersProceed();
|
||||
|
||||
if (this.enableCompass)
|
||||
mCompass.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms in or out by the given amount of zoom levels.
|
||||
*
|
||||
* @param zoomLevelDiff
|
||||
* the difference to the current zoom level.
|
||||
* @return true if the zoom level was changed, false otherwise.
|
||||
*/
|
||||
public boolean zoom(byte zoomLevelDiff) {
|
||||
|
||||
int z = mMapViewPosition.getZoomLevel() + zoomLevelDiff;
|
||||
if (zoomLevelDiff > 0) {
|
||||
// check if zoom in is possible
|
||||
if (z > getMaximumPossibleZoomLevel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (zoomLevelDiff < 0) {
|
||||
// check if zoom out is possible
|
||||
if (z < mMapZoomControls.getZoomLevelMin()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mMapViewPosition.setZoomLevel((byte) z);
|
||||
redrawTiles();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum possible zoom level.
|
||||
*/
|
||||
byte getMaximumPossibleZoomLevel() {
|
||||
return (byte) 20;
|
||||
// FIXME Math.min(mMapZoomControls.getZoomLevelMax(),
|
||||
// mMapGenerator.getZoomLevelMax());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the current center position of this MapView is valid, false otherwise.
|
||||
*/
|
||||
boolean hasValidCenter() {
|
||||
MapInfo mapInfo;
|
||||
|
||||
if (!mMapViewPosition.isValid())
|
||||
return false;
|
||||
|
||||
if ((mapInfo = mMapDatabase.getMapInfo()) == null)
|
||||
return false;
|
||||
|
||||
if (!mapInfo.boundingBox.contains(getMapPosition().getMapCenter()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
byte limitZoomLevel(byte zoom) {
|
||||
return (byte) Math.max(Math.min(zoom, getMaximumPossibleZoomLevel()),
|
||||
mMapZoomControls.getZoomLevelMin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the center and zoom level of this MapView and triggers a redraw.
|
||||
*
|
||||
* @param mapPosition
|
||||
* the new map position of this MapView.
|
||||
*/
|
||||
public void setMapCenter(MapPosition mapPosition) {
|
||||
Log.d(TAG, "setMapCenter "
|
||||
+ " lat: " + mapPosition.lat
|
||||
+ " lon: " + mapPosition.lon);
|
||||
mMapViewPosition.setMapCenter(mapPosition);
|
||||
redrawTiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the center of the MapView and triggers a redraw.
|
||||
*
|
||||
* @param geoPoint
|
||||
* the new center point of the map.
|
||||
*/
|
||||
public void setCenter(GeoPoint geoPoint) {
|
||||
MapPosition mapPosition = new MapPosition(geoPoint,
|
||||
mMapViewPosition.getZoomLevel(), 1);
|
||||
|
||||
setMapCenter(mapPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MapPosition
|
||||
*/
|
||||
public MapViewPosition getMapViewPosition() {
|
||||
return mMapViewPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* add jobs and remember MapWorkers that stuff needs to be done
|
||||
*
|
||||
* @param jobs
|
||||
* tile jobs
|
||||
*/
|
||||
public void addJobs(ArrayList<JobTile> jobs) {
|
||||
if (jobs == null) {
|
||||
mJobQueue.clear();
|
||||
return;
|
||||
}
|
||||
mJobQueue.setJobs(jobs);
|
||||
|
||||
for (int i = 0; i < mNumMapWorkers; i++) {
|
||||
MapWorker m = mMapWorkers[i];
|
||||
synchronized (m) {
|
||||
m.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mapWorkersPause(boolean wait) {
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
if (!mapWorker.isPausing())
|
||||
mapWorker.pause();
|
||||
}
|
||||
if (wait) {
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
if (!mapWorker.isPausing())
|
||||
mapWorker.awaitPausing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mapWorkersProceed() {
|
||||
for (MapWorker mapWorker : mMapWorkers)
|
||||
mapWorker.proceed();
|
||||
}
|
||||
|
||||
public void enableRotation(boolean enable) {
|
||||
enableRotation = enable;
|
||||
|
||||
if (enable) {
|
||||
enableCompass(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void enableCompass(boolean enable) {
|
||||
if (enable == this.enableCompass)
|
||||
return;
|
||||
|
||||
this.enableCompass = enable;
|
||||
|
||||
if (enable)
|
||||
enableRotation(false);
|
||||
|
||||
if (enable)
|
||||
mCompass.enable();
|
||||
else
|
||||
mCompass.disable();
|
||||
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Sets the visibility of the zoom controls.
|
||||
// *
|
||||
// * @param showZoomControls
|
||||
// * true if the zoom controls should be visible, false otherwise.
|
||||
// */
|
||||
// public void setBuiltInZoomControls(boolean showZoomControls) {
|
||||
// mMapZoomControls.setShowMapZoomControls(showZoomControls);
|
||||
//
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Sets the text scale for the map rendering. Has no effect in downloading mode.
|
||||
// *
|
||||
// * @param textScale
|
||||
// * the new text scale for the map rendering.
|
||||
// */
|
||||
// public void setTextScale(float textScale) {
|
||||
// mJobParameters = new JobParameters(mJobParameters.theme, textScale);
|
||||
// clearAndRedrawMapView();
|
||||
// }
|
||||
|
||||
// public final int
|
||||
// public Handler messageHandler = new Handler() {
|
||||
//
|
||||
// @Override
|
||||
// public void handleMessage(Message msg) {
|
||||
// switch (msg.what) {
|
||||
// // handle update
|
||||
// // .....
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// };
|
||||
|
||||
}
|
||||
259
src/org/oscim/view/MapViewPosition.java
Normal file
259
src/org/oscim/view/MapViewPosition.java
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A MapPosition stores the latitude and longitude coordinate of a MapView together with its zoom level.
|
||||
*/
|
||||
public class MapViewPosition {
|
||||
private static float MAX_SCALE = 2.0f;
|
||||
private static float MIN_SCALE = 1.0f;
|
||||
private static int MAX_ZOOMLEVEL = 16;
|
||||
|
||||
private double mLatitude;
|
||||
private double mLongitude;
|
||||
private final MapView mMapView;
|
||||
private byte mZoomLevel;
|
||||
private float mScale;
|
||||
private float mRotation;
|
||||
|
||||
MapViewPosition(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
|
||||
mLatitude = Double.NaN;
|
||||
mLongitude = Double.NaN;
|
||||
mZoomLevel = -1;
|
||||
mScale = 1;
|
||||
mRotation = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current center point of the MapView.
|
||||
*/
|
||||
public synchronized GeoPoint getMapCenter() {
|
||||
return new GeoPoint(mLatitude, mLongitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an immutable MapPosition or null, if this map position is not valid.
|
||||
* @see #isValid()
|
||||
*/
|
||||
public synchronized MapPosition getMapPosition() {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
// Log.d("MapViewPosition", "lat: " + mLatitude + " lon: " + mLongitude);
|
||||
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current zoom level of the MapView.
|
||||
*/
|
||||
public synchronized byte getZoomLevel() {
|
||||
return mZoomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current scale of the MapView.
|
||||
*/
|
||||
public synchronized float getScale() {
|
||||
return mScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MapViewPosition is valid, false otherwise.
|
||||
*/
|
||||
public synchronized boolean isValid() {
|
||||
if (Double.isNaN(mLatitude)) {
|
||||
return false;
|
||||
} else if (mLatitude < MercatorProjection.LATITUDE_MIN) {
|
||||
return false;
|
||||
} else if (mLatitude > MercatorProjection.LATITUDE_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Double.isNaN(mLongitude)) {
|
||||
return false;
|
||||
} else if (mLongitude < MercatorProjection.LONGITUDE_MIN) {
|
||||
return false;
|
||||
} else if (mLongitude > MercatorProjection.LONGITUDE_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves this MapViewPosition by the given amount of pixels.
|
||||
*
|
||||
* @param mx
|
||||
* the amount of pixels to move the map horizontally.
|
||||
* @param my
|
||||
* the amount of pixels to move the map vertically.
|
||||
*/
|
||||
public synchronized void moveMap(float mx, float my) {
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
double dx, dy;
|
||||
|
||||
if (mMapView.enableRotation || mMapView.enableCompass) {
|
||||
float rad = (float) Math.toRadians(mRotation);
|
||||
dx = mx / mScale;
|
||||
dy = my / mScale;
|
||||
|
||||
double x = dx * FloatMath.cos(rad) + dy * -FloatMath.sin(rad);
|
||||
double y = dx * FloatMath.sin(rad) + dy * FloatMath.cos(rad);
|
||||
|
||||
dx = pixelX - x;
|
||||
dy = pixelY - y;
|
||||
}
|
||||
else {
|
||||
dx = pixelX - mx / mScale;
|
||||
dy = pixelY - my / mScale;
|
||||
}
|
||||
mLatitude = MercatorProjection.pixelYToLatitude(dy, mZoomLevel);
|
||||
mLatitude = MercatorProjection.limitLatitude(mLatitude);
|
||||
|
||||
mLongitude = MercatorProjection.pixelXToLongitude(dx, mZoomLevel);
|
||||
|
||||
mLongitude = MercatorProjection.wrapLongitude(mLongitude);
|
||||
// mLongitude = MercatorProjection.limitLongitude(mLongitude);
|
||||
}
|
||||
|
||||
public synchronized void rotateMap(float angle, float cx, float cy) {
|
||||
moveMap(cx, cy);
|
||||
Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation - angle));
|
||||
mRotation -= angle;
|
||||
}
|
||||
|
||||
public void setRotation(float f) {
|
||||
mRotation = f;
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(GeoPoint geoPoint) {
|
||||
mLatitude = MercatorProjection.limitLatitude(geoPoint.getLatitude());
|
||||
mLongitude = MercatorProjection.limitLongitude(geoPoint.getLongitude());
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(MapPosition mapPosition) {
|
||||
mLatitude = MercatorProjection.limitLatitude(mapPosition.lat);
|
||||
mLongitude = MercatorProjection.limitLongitude(mapPosition.lon);
|
||||
mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
|
||||
}
|
||||
|
||||
synchronized void setZoomLevel(byte zoomLevel) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
|
||||
}
|
||||
|
||||
synchronized void setScale(float scale) {
|
||||
mScale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale
|
||||
* ...
|
||||
* @param pivotX
|
||||
* ...
|
||||
* @param pivotY
|
||||
* ...
|
||||
*/
|
||||
public synchronized void scaleMap(float scale, float pivotX, float pivotY) {
|
||||
if (pivotY != 0 || pivotY != 0)
|
||||
moveMap(pivotX * (1.0f - scale),
|
||||
pivotY * (1.0f - scale));
|
||||
|
||||
float s = mScale * scale;
|
||||
|
||||
if (s >= MAX_SCALE) {
|
||||
if (s > 8)
|
||||
return;
|
||||
|
||||
if (mZoomLevel <= MAX_ZOOMLEVEL) {
|
||||
byte z = (byte) FloatMath.sqrt(s);
|
||||
mZoomLevel += z;
|
||||
s *= 1.0f / (1 << z);
|
||||
}
|
||||
} else if (s < MIN_SCALE) {
|
||||
byte z = (byte) FloatMath.sqrt(1 / s);
|
||||
if (z != 0 && mZoomLevel == 1)
|
||||
return;
|
||||
mZoomLevel -= z;
|
||||
s *= 1 << z;
|
||||
}
|
||||
|
||||
mScale = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms in or out by the given amount of zoom levels.
|
||||
*
|
||||
* @param zoomLevelDiff
|
||||
* the difference to the current zoom level.
|
||||
* @param s
|
||||
* scale between min/max zoom
|
||||
* @return true if the zoom level was changed, false otherwise.
|
||||
*/
|
||||
// public boolean zoom(byte zoomLevelDiff, float s) {
|
||||
// float scale = s;
|
||||
//
|
||||
// if (zoomLevelDiff > 0) {
|
||||
// // check if zoom in is possible
|
||||
// if (mMapViewPosition.getZoomLevel() + zoomLevelDiff > getMaximumPossibleZoomLevel()) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// scale *= 1.0f / (1 << zoomLevelDiff);
|
||||
// } else if (zoomLevelDiff < 0) {
|
||||
// // check if zoom out is possible
|
||||
// if (mMapViewPosition.getZoomLevel() + zoomLevelDiff < mMapZoomControls.getZoomLevelMin()) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// scale *= 1 << -zoomLevelDiff;
|
||||
// }
|
||||
//
|
||||
// if (scale == 0)
|
||||
// scale = 1;
|
||||
// // else
|
||||
// // scale = Math.round(256.0f * scale) / 256.0f;
|
||||
//
|
||||
// mMapViewPosition.setZoomLevel((byte) (mMapViewPosition.getZoomLevel() + zoomLevelDiff));
|
||||
//
|
||||
// // mapZoomControls.onZoomLevelChange(mapViewPosition.getZoomLevel());
|
||||
//
|
||||
// // zoomAnimator.setParameters(zoomStart, matrixScaleFactor,
|
||||
// // getWidth() >> 1, getHeight() >> 1);
|
||||
// // zoomAnimator.startAnimation();
|
||||
//
|
||||
// // if (scale > MAX_ZOOM) {
|
||||
// // scale = MAX_ZOOM;
|
||||
// // }
|
||||
//
|
||||
// if (zoomLevelDiff != 0 || mZoomFactor != scale) {
|
||||
// mZoomFactor = scale;
|
||||
// redrawTiles();
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
|
||||
}
|
||||
125
src/org/oscim/view/MapViewProjection.java
Normal file
125
src/org/oscim/view/MapViewProjection.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
class MapViewProjection implements Projection {
|
||||
private static final String INVALID_MAP_VIEW_DIMENSIONS = "invalid MapView dimensions";
|
||||
|
||||
private final MapView mMapView;
|
||||
|
||||
MapViewProjection(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint fromPixels(int x, int y) {
|
||||
if (mMapView.getWidth() <= 0 || mMapView.getHeight() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
|
||||
// calculate the pixel coordinates of the top left corner
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mapPosition);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mapPosition);
|
||||
pixelX -= mMapView.getWidth() >> 1;
|
||||
pixelY -= mMapView.getHeight() >> 1;
|
||||
|
||||
// convert the pixel coordinates to a GeoPoint and return it
|
||||
return new GeoPoint(MercatorProjection.pixelYToLatitude(pixelY + y,
|
||||
mapPosition.zoomLevel),
|
||||
MercatorProjection.pixelXToLongitude(pixelX + x, mapPosition.zoomLevel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLatitudeSpan() {
|
||||
if (mMapView.getWidth() > 0 && mMapView.getWidth() > 0) {
|
||||
GeoPoint top = fromPixels(0, 0);
|
||||
GeoPoint bottom = fromPixels(0, mMapView.getHeight());
|
||||
return Math.abs(top.latitudeE6 - bottom.latitudeE6);
|
||||
}
|
||||
throw new IllegalStateException(INVALID_MAP_VIEW_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLongitudeSpan() {
|
||||
if (mMapView.getWidth() > 0 && mMapView.getWidth() > 0) {
|
||||
GeoPoint left = fromPixels(0, 0);
|
||||
GeoPoint right = fromPixels(mMapView.getWidth(), 0);
|
||||
return Math.abs(left.longitudeE6 - right.longitudeE6);
|
||||
}
|
||||
throw new IllegalStateException(INVALID_MAP_VIEW_DIMENSIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float metersToPixels(float meters, byte zoom) {
|
||||
double latitude = mMapView.getMapPosition().getMapCenter().getLatitude();
|
||||
double groundResolution = MercatorProjection.calculateGroundResolution(latitude,
|
||||
zoom);
|
||||
return (float) (meters * (1 / groundResolution));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point toPixels(GeoPoint in, Point out) {
|
||||
if (mMapView.getWidth() <= 0 || mMapView.getHeight() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
|
||||
// calculate the pixel coordinates of the top left corner
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mapPosition);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mapPosition);
|
||||
pixelX -= mMapView.getWidth() >> 1;
|
||||
pixelY -= mMapView.getHeight() >> 1;
|
||||
|
||||
if (out == null) {
|
||||
// create a new point and return it
|
||||
return new Point(
|
||||
(int) (MercatorProjection.longitudeToPixelX(in.getLongitude(),
|
||||
mapPosition.zoomLevel) - pixelX),
|
||||
(int) (MercatorProjection.latitudeToPixelY(in.getLatitude(),
|
||||
mapPosition.zoomLevel) - pixelY));
|
||||
}
|
||||
|
||||
// reuse the existing point
|
||||
out.x = (int) (MercatorProjection.longitudeToPixelX(in.getLongitude(),
|
||||
mapPosition.zoomLevel) - pixelX);
|
||||
out.y = (int) (MercatorProjection.latitudeToPixelY(in.getLatitude(),
|
||||
mapPosition.zoomLevel) - pixelY);
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point toPoint(GeoPoint in, Point out, byte zoom) {
|
||||
if (out == null) {
|
||||
// create a new point and return it
|
||||
return new Point((int) MercatorProjection.longitudeToPixelX(
|
||||
in.getLongitude(), zoom),
|
||||
(int) MercatorProjection.latitudeToPixelY(in.getLatitude(), zoom));
|
||||
}
|
||||
|
||||
// reuse the existing point
|
||||
out.x = (int) MercatorProjection.longitudeToPixelX(in.getLongitude(), zoom);
|
||||
out.y = (int) MercatorProjection.latitudeToPixelY(in.getLatitude(), zoom);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
313
src/org/oscim/view/MapZoomControls.java
Normal file
313
src/org/oscim/view/MapZoomControls.java
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.ZoomControls;
|
||||
|
||||
/**
|
||||
* A MapZoomControls instance displays buttons for zooming in and out in a map.
|
||||
*/
|
||||
public class MapZoomControls {
|
||||
private static class ZoomControlsHideHandler extends Handler {
|
||||
private final ZoomControls mZoomControls;
|
||||
|
||||
ZoomControlsHideHandler(ZoomControls zoomControls) {
|
||||
super();
|
||||
mZoomControls = zoomControls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
mZoomControls.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ZoomInClickListener implements View.OnClickListener {
|
||||
private final MapView mMapView;
|
||||
|
||||
ZoomInClickListener(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mMapView.zoom((byte) 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ZoomOutClickListener implements View.OnClickListener {
|
||||
private final MapView mMapView;
|
||||
|
||||
ZoomOutClickListener(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mMapView.zoom((byte) -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link Gravity} of the zoom controls.
|
||||
*/
|
||||
private static final int DEFAULT_ZOOM_CONTROLS_GRAVITY = Gravity.BOTTOM
|
||||
| Gravity.RIGHT;
|
||||
|
||||
/**
|
||||
* Default maximum zoom level.
|
||||
*/
|
||||
private static final byte DEFAULT_ZOOM_LEVEL_MAX = 22;
|
||||
|
||||
/**
|
||||
* Default minimum zoom level.
|
||||
*/
|
||||
private static final byte DEFAULT_ZOOM_LEVEL_MIN = 0;
|
||||
|
||||
/**
|
||||
* Message code for the handler to hide the zoom controls.
|
||||
*/
|
||||
private static final int MSG_ZOOM_CONTROLS_HIDE = 0;
|
||||
|
||||
/**
|
||||
* Horizontal padding for the zoom controls.
|
||||
*/
|
||||
private static final int ZOOM_CONTROLS_HORIZONTAL_PADDING = 5;
|
||||
|
||||
/**
|
||||
* Delay in milliseconds after which the zoom controls disappear.
|
||||
*/
|
||||
private static final long ZOOM_CONTROLS_TIMEOUT = ViewConfiguration
|
||||
.getZoomControlsTimeout();
|
||||
|
||||
private boolean mGravityChanged;
|
||||
private boolean mShowMapZoomControls;
|
||||
private final ZoomControls mZoomControls;
|
||||
private int mZoomControlsGravity;
|
||||
private final Handler mZoomControlsHideHandler;
|
||||
private byte mZoomLevelMax;
|
||||
private byte mZoomLevelMin;
|
||||
|
||||
MapZoomControls(Context context, final MapView mapView) {
|
||||
mZoomControls = new ZoomControls(context);
|
||||
mShowMapZoomControls = true;
|
||||
mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX;
|
||||
mZoomLevelMin = DEFAULT_ZOOM_LEVEL_MIN;
|
||||
mZoomControls.setVisibility(View.GONE);
|
||||
mZoomControlsGravity = DEFAULT_ZOOM_CONTROLS_GRAVITY;
|
||||
|
||||
mZoomControls.setOnZoomInClickListener(new ZoomInClickListener(mapView));
|
||||
mZoomControls.setOnZoomOutClickListener(new ZoomOutClickListener(mapView));
|
||||
mZoomControlsHideHandler = new ZoomControlsHideHandler(mZoomControls);
|
||||
|
||||
// int wrapContent = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
// LayoutParams layoutParams = new LayoutParams(wrapContent, wrapContent);
|
||||
// mapView.addView(zoomControls, layoutParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current gravity for the placing of the zoom controls.
|
||||
* @see Gravity
|
||||
*/
|
||||
public int getZoomControlsGravity() {
|
||||
return mZoomControlsGravity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum zoom level of the map.
|
||||
*/
|
||||
public byte getZoomLevelMax() {
|
||||
return mZoomLevelMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minimum zoom level of the map.
|
||||
*/
|
||||
public byte getZoomLevelMin() {
|
||||
return mZoomLevelMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the zoom controls are visible, false otherwise.
|
||||
*/
|
||||
public boolean isShowMapZoomControls() {
|
||||
return mShowMapZoomControls;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param showMapZoomControls
|
||||
* true if the zoom controls should be visible, false otherwise.
|
||||
*/
|
||||
public void setShowMapZoomControls(boolean showMapZoomControls) {
|
||||
mShowMapZoomControls = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the gravity for the placing of the zoom controls. Supported values are {@link Gravity#TOP},
|
||||
* {@link Gravity#CENTER_VERTICAL}, {@link Gravity#BOTTOM}, {@link Gravity#LEFT}, {@link Gravity#CENTER_HORIZONTAL}
|
||||
* and {@link Gravity#RIGHT}.
|
||||
*
|
||||
* @param zoomControlsGravity
|
||||
* a combination of {@link Gravity} constants describing the desired placement.
|
||||
*/
|
||||
public void setZoomControlsGravity(int zoomControlsGravity) {
|
||||
if (mZoomControlsGravity != zoomControlsGravity) {
|
||||
mZoomControlsGravity = zoomControlsGravity;
|
||||
mGravityChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum zoom level of the map.
|
||||
* <p>
|
||||
* The maximum possible zoom level of the MapView depends also on the current {@link IMapGenerator}. For example,
|
||||
* downloading map tiles may only be possible up to a certain zoom level. Setting a higher maximum zoom level has no
|
||||
* effect in this case.
|
||||
*
|
||||
* @param zoomLevelMax
|
||||
* the maximum zoom level.
|
||||
* @throws IllegalArgumentException
|
||||
* if the maximum zoom level is smaller than the current minimum zoom level.
|
||||
*/
|
||||
public void setZoomLevelMax(byte zoomLevelMax) {
|
||||
if (zoomLevelMax < mZoomLevelMin) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
mZoomLevelMax = zoomLevelMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum zoom level of the map.
|
||||
*
|
||||
* @param zoomLevelMin
|
||||
* the minimum zoom level.
|
||||
* @throws IllegalArgumentException
|
||||
* if the minimum zoom level is larger than the current maximum zoom level.
|
||||
*/
|
||||
public void setZoomLevelMin(byte zoomLevelMin) {
|
||||
if (zoomLevelMin > mZoomLevelMax) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
mZoomLevelMin = zoomLevelMin;
|
||||
}
|
||||
|
||||
private int calculatePositionLeft(int left, int right, int zoomControlsWidth) {
|
||||
int gravity = mZoomControlsGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
switch (gravity) {
|
||||
case Gravity.LEFT:
|
||||
return ZOOM_CONTROLS_HORIZONTAL_PADDING;
|
||||
|
||||
case Gravity.CENTER_HORIZONTAL:
|
||||
return (right - left - zoomControlsWidth) / 2;
|
||||
|
||||
case Gravity.RIGHT:
|
||||
return right - left - zoomControlsWidth
|
||||
- ZOOM_CONTROLS_HORIZONTAL_PADDING;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("unknown horizontal gravity: " + gravity);
|
||||
}
|
||||
|
||||
private int calculatePositionTop(int top, int bottom, int zoomControlsHeight) {
|
||||
int gravity = mZoomControlsGravity & Gravity.VERTICAL_GRAVITY_MASK;
|
||||
switch (gravity) {
|
||||
case Gravity.TOP:
|
||||
return 0;
|
||||
|
||||
case Gravity.CENTER_VERTICAL:
|
||||
return (bottom - top - zoomControlsHeight) / 2;
|
||||
|
||||
case Gravity.BOTTOM:
|
||||
return bottom - top - zoomControlsHeight;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("unknown vertical gravity: " + gravity);
|
||||
}
|
||||
|
||||
private void showZoomControls() {
|
||||
mZoomControlsHideHandler.removeMessages(MSG_ZOOM_CONTROLS_HIDE);
|
||||
if (mZoomControls.getVisibility() != View.VISIBLE) {
|
||||
mZoomControls.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void showZoomControlsWithTimeout() {
|
||||
showZoomControls();
|
||||
mZoomControlsHideHandler.sendEmptyMessageDelayed(MSG_ZOOM_CONTROLS_HIDE,
|
||||
ZOOM_CONTROLS_TIMEOUT);
|
||||
}
|
||||
|
||||
int getMeasuredHeight() {
|
||||
return mZoomControls.getMeasuredHeight();
|
||||
}
|
||||
|
||||
int getMeasuredWidth() {
|
||||
return mZoomControls.getMeasuredWidth();
|
||||
}
|
||||
|
||||
void measure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
mZoomControls.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
if (!changed && !mGravityChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
int zoomControlsWidth = mZoomControls.getMeasuredWidth();
|
||||
int zoomControlsHeight = mZoomControls.getMeasuredHeight();
|
||||
|
||||
int positionLeft = calculatePositionLeft(left, right, zoomControlsWidth);
|
||||
int positionTop = calculatePositionTop(top, bottom, zoomControlsHeight);
|
||||
int positionRight = positionLeft + zoomControlsWidth;
|
||||
int positionBottom = positionTop + zoomControlsHeight;
|
||||
|
||||
mZoomControls.layout(positionLeft, positionTop, positionRight, positionBottom);
|
||||
mGravityChanged = false;
|
||||
}
|
||||
|
||||
void onMapViewTouchEvent(int action) {
|
||||
if (mShowMapZoomControls) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
showZoomControls();
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
showZoomControlsWithTimeout();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
showZoomControlsWithTimeout();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onZoomLevelChange(int zoomLevel) {
|
||||
boolean zoomInEnabled = zoomLevel < mZoomLevelMax;
|
||||
boolean zoomOutEnabled = zoomLevel > mZoomLevelMin;
|
||||
|
||||
mZoomControls.setIsZoomInEnabled(zoomInEnabled);
|
||||
mZoomControls.setIsZoomOutEnabled(zoomOutEnabled);
|
||||
}
|
||||
}
|
||||
91
src/org/oscim/view/Projection.java
Normal file
91
src/org/oscim/view/Projection.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
/**
|
||||
* A Projection translates between the pixel coordinate system on the screen and geographical points on the earth. To
|
||||
* retrieve the currently used Projection for a given MapView, call the {@link MapView#getProjection()} method.
|
||||
*/
|
||||
public interface Projection {
|
||||
/**
|
||||
* Translates the given screen coordinates to a {@link GeoPoint}. If the corresponding MapView has no valid
|
||||
* dimensions (width and height > 0), null is returned.
|
||||
*
|
||||
* @param x
|
||||
* the pixel x coordinate on the screen.
|
||||
* @param y
|
||||
* the pixel y coordinate on the screen.
|
||||
* @return a new {@link GeoPoint} or null, if the corresponding MapView has no valid dimensions.
|
||||
*/
|
||||
GeoPoint fromPixels(int x, int y);
|
||||
|
||||
/**
|
||||
* @return the latitude span from the top to the bottom of the map in microdegrees (degrees * 10^6).
|
||||
* @throws IllegalStateException
|
||||
* if the MapView dimensions are not valid (width and height > 0).
|
||||
*/
|
||||
int getLatitudeSpan();
|
||||
|
||||
/**
|
||||
* @return the longitude span from the left to the right of the map in microdegrees (degrees * 10^6).
|
||||
* @throws IllegalStateException
|
||||
* if the MapView dimensions are not valid (width and height > 0).
|
||||
*/
|
||||
int getLongitudeSpan();
|
||||
|
||||
/**
|
||||
* Converts the given distance in meters at the given zoom level to the corresponding number of horizontal pixels.
|
||||
* The calculation is carried out at the current latitude coordinate.
|
||||
*
|
||||
* @param meters
|
||||
* the distance in meters.
|
||||
* @param zoomLevel
|
||||
* the zoom level at which the distance should be calculated.
|
||||
* @return the number of pixels at the current map position and the given zoom level.
|
||||
*/
|
||||
float metersToPixels(float meters, byte zoomLevel);
|
||||
|
||||
/**
|
||||
* Translates the given {@link GeoPoint} to relative pixel coordinates on the screen. If the corresponding MapView
|
||||
* has no valid dimensions (width and height > 0), null is returned.
|
||||
*
|
||||
* @param in
|
||||
* the geographical point to convert.
|
||||
* @param out
|
||||
* an already existing object to use for the output. If this parameter is null, a new Point object will
|
||||
* be created and returned.
|
||||
* @return a Point which is relative to the top-left of the MapView or null, if the corresponding MapView has no
|
||||
* valid dimensions.
|
||||
*/
|
||||
Point toPixels(GeoPoint in, Point out);
|
||||
|
||||
/**
|
||||
* Translates the given {@link GeoPoint} to absolute pixel coordinates on the world map.
|
||||
*
|
||||
* @param in
|
||||
* the geographical point to convert.
|
||||
* @param out
|
||||
* an already existing object to use for the output. If this parameter is null, a new Point object will
|
||||
* be created and returned.
|
||||
* @param zoomLevel
|
||||
* the zoom level at which the point should be converted.
|
||||
* @return a Point which is relative to the top-left of the world map.
|
||||
*/
|
||||
Point toPoint(GeoPoint in, Point out, byte zoomLevel);
|
||||
}
|
||||
544
src/org/oscim/view/TouchHandler.java
Normal file
544
src/org/oscim/view/TouchHandler.java
Normal file
@@ -0,0 +1,544 @@
|
||||
/*
|
||||
* 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.oscim.view;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Scroller;
|
||||
|
||||
/**
|
||||
* Implementation for multi-touch capable devices.
|
||||
*/
|
||||
public class TouchHandler {
|
||||
private static final int INVALID_POINTER_ID = -1;
|
||||
|
||||
/* package */final MapView mMapView;
|
||||
/* package */final MapViewPosition mMapPosition;
|
||||
|
||||
private final float mMapMoveDelta;
|
||||
private boolean mMoveStart;
|
||||
private boolean mRotationStart;
|
||||
private float mPosX;
|
||||
private float mPosY;
|
||||
private double mAngle;
|
||||
|
||||
private int mActivePointerId;
|
||||
|
||||
private final ScaleGestureDetector mScaleGestureDetector;
|
||||
private final GestureDetector mGestureDetector;
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* the Context
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public TouchHandler(Context context, MapView mapView) {
|
||||
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
|
||||
mMapView = mapView;
|
||||
mMapPosition = mapView.getMapPosition();
|
||||
mMapMoveDelta = viewConfiguration.getScaledTouchSlop();
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener());
|
||||
mGestureDetector = new GestureDetector(context, new MapGestureDetector());
|
||||
|
||||
}
|
||||
|
||||
private static int getAction(MotionEvent motionEvent) {
|
||||
return motionEvent.getAction() & MotionEvent.ACTION_MASK;
|
||||
}
|
||||
|
||||
private boolean onActionCancel() {
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onActionDown(MotionEvent event) {
|
||||
mPosX = event.getX();
|
||||
mPosY = event.getY();
|
||||
mMoveStart = false;
|
||||
mRotationStart = false;
|
||||
// save the ID of the pointer
|
||||
mActivePointerId = event.getPointerId(0);
|
||||
// Log.d("...", "set active pointer" + mActivePointerId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean mScaling = false;
|
||||
|
||||
private boolean onActionMove(MotionEvent event) {
|
||||
int pointerIndex = event.findPointerIndex(mActivePointerId);
|
||||
|
||||
// calculate the distance between previous and current position
|
||||
float moveX = event.getX(pointerIndex) - mPosX;
|
||||
float moveY = event.getY(pointerIndex) - mPosY;
|
||||
|
||||
boolean scaling = mScaleGestureDetector.isInProgress();
|
||||
if (!mScaling) {
|
||||
mScaling = scaling;
|
||||
}
|
||||
if (!scaling && !mMoveStart) {
|
||||
|
||||
if (Math.abs(moveX) > 3 * mMapMoveDelta
|
||||
|| Math.abs(moveY) > 3 * mMapMoveDelta) {
|
||||
// the map movement threshold has been reached
|
||||
// longPressDetector.pressStop();
|
||||
mMoveStart = true;
|
||||
|
||||
// save the position of the event
|
||||
mPosX = event.getX(pointerIndex);
|
||||
mPosY = event.getY(pointerIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mMapView.enableRotation) {
|
||||
if (multi > 0) {
|
||||
double x1 = event.getX(0);
|
||||
double x2 = event.getX(1);
|
||||
double y1 = event.getY(0);
|
||||
double y2 = event.getY(1);
|
||||
|
||||
double dx = x1 - x2;
|
||||
double dy = y1 - y2;
|
||||
|
||||
double rad = Math.atan2(dy, dx);
|
||||
|
||||
// focus point relative to center
|
||||
double cx = (mMapView.getWidth() >> 1) - (x1 + x2) / 2;
|
||||
double cy = (mMapView.getHeight() >> 1) - (y1 + y2) / 2;
|
||||
double r = rad - mAngle;
|
||||
|
||||
double rsin = Math.sin(r);
|
||||
double rcos = Math.cos(r);
|
||||
|
||||
float x = (float) (cx * rcos + cy * -rsin - cx);
|
||||
float y = (float) (cx * rsin + cy * rcos - cy);
|
||||
|
||||
// Log.d("...", "move " + x + " " + y + " " + cx + " " + cy);
|
||||
|
||||
if (!mRotationStart) {
|
||||
if (Math.abs(rad - mAngle) > 0.001)
|
||||
mRotationStart = true;
|
||||
}
|
||||
else {
|
||||
mMapPosition.rotateMap((float) Math.toDegrees(rad - mAngle), x, y);
|
||||
mAngle = rad;
|
||||
mMapView.redrawTiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
// save the position of the event
|
||||
mPosX = event.getX(pointerIndex);
|
||||
mPosY = event.getY(pointerIndex);
|
||||
|
||||
if (scaling) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mMapPosition.moveMap(moveX, moveY);
|
||||
mMapView.redrawTiles();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int multi = 0;
|
||||
|
||||
private boolean onActionPointerDown(MotionEvent event) {
|
||||
// longPressDetector.pressStop();
|
||||
// multiTouchDownTime = motionEvent.getEventTime();
|
||||
multi++;
|
||||
if (multi == 1) {
|
||||
double dx = event.getX(0) - event.getX(1);
|
||||
double dy = event.getY(0) - event.getY(1);
|
||||
mAngle = Math.atan2(dy, dx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onActionPointerUp(MotionEvent motionEvent) {
|
||||
|
||||
// extract the index of the pointer that left the touch sensor
|
||||
int pointerIndex = (motionEvent.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
|
||||
if (motionEvent.getPointerId(pointerIndex) == mActivePointerId) {
|
||||
// the active pointer has gone up, choose a new one
|
||||
if (pointerIndex == 0) {
|
||||
pointerIndex = 1;
|
||||
} else {
|
||||
pointerIndex = 0;
|
||||
}
|
||||
// save the position of the event
|
||||
mPosX = motionEvent.getX(pointerIndex);
|
||||
mPosY = motionEvent.getY(pointerIndex);
|
||||
mActivePointerId = motionEvent.getPointerId(pointerIndex);
|
||||
}
|
||||
multi--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motionEvent
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
private boolean onActionUp(MotionEvent motionEvent) {
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
mScaling = false;
|
||||
multi = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param event
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
|
||||
// workaround for a bug in the ScaleGestureDetector, see Android issue
|
||||
// #12976
|
||||
// if (event.getAction() != MotionEvent.ACTION_MOVE
|
||||
// || event.getPointerCount() > 1) {
|
||||
mScaleGestureDetector.onTouchEvent(event);
|
||||
// }
|
||||
|
||||
if (!mScaling)
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
|
||||
int action = getAction(event);
|
||||
boolean ret = false;
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
ret = onActionDown(event);
|
||||
} else if (action == MotionEvent.ACTION_MOVE) {
|
||||
ret = onActionMove(event);
|
||||
} else if (action == MotionEvent.ACTION_UP) {
|
||||
ret = onActionUp(event);
|
||||
} else if (action == MotionEvent.ACTION_CANCEL) {
|
||||
ret = onActionCancel();
|
||||
} else if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
return onActionPointerDown(event);
|
||||
} else if (action == MotionEvent.ACTION_POINTER_UP) {
|
||||
ret = onActionPointerUp(event);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
class MapGestureDetector extends SimpleOnGestureListener {
|
||||
private Scroller mScroller;
|
||||
private float mPrevX, mPrevY, mPrevScale;
|
||||
private CountDownTimer mTimer = null;
|
||||
private boolean fling = false;
|
||||
|
||||
public MapGestureDetector() {
|
||||
mScroller = new Scroller(mMapView.getContext(),
|
||||
new android.view.animation.LinearInterpolator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
if (fling) {
|
||||
mScroller.forceFinished(true);
|
||||
|
||||
if (mTimer != null) {
|
||||
mTimer.cancel();
|
||||
mTimer = null;
|
||||
}
|
||||
fling = false;
|
||||
}
|
||||
// Log.d("mapsforge", "onDown");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean scroll() {
|
||||
if (mScroller.isFinished()) {
|
||||
return false;
|
||||
}
|
||||
mScroller.computeScrollOffset();
|
||||
|
||||
float moveX = mScroller.getCurrX() - mPrevX;
|
||||
float moveY = mScroller.getCurrY() - mPrevY;
|
||||
|
||||
if (moveX >= 1 || moveY >= 1 || moveX <= -1 || moveY <= -1) {
|
||||
mMapPosition.moveMap(moveX, moveY);
|
||||
mMapView.redrawTiles();
|
||||
mPrevX = mScroller.getCurrX();
|
||||
mPrevY = mScroller.getCurrY();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
||||
float velocityY) {
|
||||
int w = Tile.TILE_SIZE * 20;
|
||||
int h = Tile.TILE_SIZE * 20;
|
||||
mPrevX = 0;
|
||||
mPrevY = 0;
|
||||
|
||||
if (mTimer != null) {
|
||||
mTimer.cancel();
|
||||
mTimer = null;
|
||||
}
|
||||
|
||||
mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2,
|
||||
-w, w, -h, h);
|
||||
|
||||
// animate for two seconds
|
||||
mTimer = new CountDownTimer(1500, 50) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// do nothing
|
||||
}
|
||||
}.start();
|
||||
fling = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private DecelerateInterpolator mBounce = new DecelerateInterpolator();
|
||||
|
||||
private boolean mZooutOut = true;
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
Log.d("mapsforge", "long press");
|
||||
|
||||
// mMapView.zoom((byte) -1);
|
||||
|
||||
mPrevScale = 0;
|
||||
|
||||
mTimer = new CountDownTimer((int) mScaleDuration, 30) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scale2(tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
scale2(0);
|
||||
|
||||
}
|
||||
}.start();
|
||||
|
||||
}
|
||||
|
||||
boolean scale2(long tick) {
|
||||
|
||||
if (mPrevScale >= 1) {
|
||||
mTimer = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
float adv = (mScaleDuration - tick) / mScaleDuration;
|
||||
adv = mBounce.getInterpolation(adv);
|
||||
|
||||
float scale = adv - mPrevScale;
|
||||
mPrevScale += scale;
|
||||
|
||||
if (mZooutOut) {
|
||||
mMapPosition.scaleMap(1 - scale, 0, 0);
|
||||
}
|
||||
// } else {
|
||||
// mMapPosition.scaleMap(1 + scale, mFocusX, mFocusY);
|
||||
// }
|
||||
|
||||
mMapView.redrawTiles();
|
||||
|
||||
if (tick == 0)
|
||||
mTimer = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final float mScaleDuration = 300;
|
||||
|
||||
boolean scale(long tick) {
|
||||
|
||||
fling = true;
|
||||
if (mPrevScale >= 1)
|
||||
return false;
|
||||
float adv = (mScaleDuration - tick) / mScaleDuration;
|
||||
adv = mBounce.getInterpolation(adv);
|
||||
|
||||
float scale = adv - mPrevScale;
|
||||
mPrevScale += scale;
|
||||
scale += 1;
|
||||
adv += 1;
|
||||
|
||||
if (scale > 1) {
|
||||
mMapPosition.scaleMap(scale, mPrevX / adv, mPrevY / adv);
|
||||
mMapView.redrawTiles();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
// Log.d("mapsforge", "double tap");
|
||||
|
||||
mPrevX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f;
|
||||
mPrevY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f;
|
||||
mPrevScale = 0;
|
||||
|
||||
mTimer = new CountDownTimer((int) mScaleDuration, 30) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scale(tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
scale(0);
|
||||
}
|
||||
}.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
|
||||
private float mCenterX;
|
||||
private float mCenterY;
|
||||
private float mFocusX;
|
||||
private float mFocusY;
|
||||
private float mScale;
|
||||
private boolean mBeginScale;
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector gd) {
|
||||
|
||||
mScale = gd.getScaleFactor();
|
||||
mFocusX = gd.getFocusX() - mCenterX;
|
||||
mFocusY = gd.getFocusY() - mCenterY;
|
||||
|
||||
mSumScale *= mScale;
|
||||
|
||||
mTimeEnd = SystemClock.elapsedRealtime();
|
||||
|
||||
if (!mBeginScale) {
|
||||
if (mTimeEnd - mTimeStart > 100) {
|
||||
mBeginScale = true;
|
||||
mScale = mSumScale;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
mMapPosition.scaleMap(mScale, mFocusX, mFocusY);
|
||||
mMapView.redrawTiles();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private long mTimeStart;
|
||||
private long mTimeEnd;
|
||||
private float mSumScale;
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector gd) {
|
||||
mTimeEnd = mTimeStart = SystemClock.elapsedRealtime();
|
||||
mSumScale = 1;
|
||||
mBeginScale = false;
|
||||
mCenterX = mMapView.getWidth() >> 1;
|
||||
mCenterY = mMapView.getHeight() >> 1;
|
||||
mScale = 1;
|
||||
|
||||
if (mTimer != null) {
|
||||
mTimer.cancel();
|
||||
mTimer = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector gd) {
|
||||
// Log.d("ScaleListener", "Sum " + mSumScale + " " + (mTimeEnd - mTimeStart));
|
||||
|
||||
if (mTimer == null && mTimeEnd - mTimeStart < 150
|
||||
&& (mSumScale < 0.99 || mSumScale > 1.01)) {
|
||||
|
||||
mPrevScale = 0;
|
||||
|
||||
mZooutOut = mSumScale < 0.99;
|
||||
|
||||
mTimer = new CountDownTimer((int) mScaleDuration, 30) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scale(tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
scale(0);
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
private DecelerateInterpolator mBounce = new DecelerateInterpolator();
|
||||
private float mPrevScale;
|
||||
private CountDownTimer mTimer;
|
||||
boolean mZooutOut;
|
||||
private final float mScaleDuration = 350;
|
||||
|
||||
boolean scale(long tick) {
|
||||
|
||||
if (mPrevScale >= 1) {
|
||||
mTimer = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
float adv = (mScaleDuration - tick) / mScaleDuration;
|
||||
adv = mBounce.getInterpolation(adv);
|
||||
|
||||
float scale = adv - mPrevScale;
|
||||
mPrevScale += scale;
|
||||
|
||||
if (mZooutOut) {
|
||||
mMapPosition.scaleMap(1 - scale, 0, 0);
|
||||
} else {
|
||||
mMapPosition.scaleMap(1 + scale, mFocusX, mFocusY);
|
||||
}
|
||||
|
||||
mMapView.redrawTiles();
|
||||
|
||||
if (tick == 0)
|
||||
mTimer = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
489
src/org/oscim/view/glrenderer/LineLayer.java
Normal file
489
src/org/oscim/view/glrenderer/LineLayer.java
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.util.FloatMath;
|
||||
|
||||
class LineLayer {
|
||||
|
||||
private static final float COORD_SCALE = MapRenderer.COORD_MULTIPLIER;
|
||||
// scale factor mapping extrusion vector to short values
|
||||
private static final float DIR_SCALE = 2048;
|
||||
// mask for packing last two bits of extrusion vector with texture coordinates
|
||||
private static final int DIR_MASK = 0xFFFFFFFC;
|
||||
|
||||
// next layer
|
||||
LineLayer next;
|
||||
|
||||
// lines referenced by this outline layer
|
||||
LineLayer outlines;
|
||||
|
||||
Line line;
|
||||
float width;
|
||||
boolean isOutline;
|
||||
int layer;
|
||||
|
||||
ShortItem pool;
|
||||
protected ShortItem curItem;
|
||||
|
||||
// number of vertices this layer holds
|
||||
int verticesCnt;
|
||||
// vertices offset of this layer in VBO
|
||||
int offset;
|
||||
|
||||
LineLayer(int layer, Line line, float width, boolean outline) {
|
||||
this.layer = layer;
|
||||
this.width = width;
|
||||
this.line = line;
|
||||
this.isOutline = outline;
|
||||
}
|
||||
|
||||
void addOutline(LineLayer link) {
|
||||
for (LineLayer l = outlines; l != null; l = l.outlines)
|
||||
if (link == l)
|
||||
return;
|
||||
|
||||
link.outlines = outlines;
|
||||
outlines = link;
|
||||
}
|
||||
|
||||
/*
|
||||
* line extrusion is based on code from GLMap (https://github.com/olofsj/GLMap/) by olofsj
|
||||
*/
|
||||
|
||||
void addLine(float[] points, short[] index) {
|
||||
float x, y, nextX, nextY, prevX, prevY;
|
||||
float a, ux, uy, vx, vy, wx, wy;
|
||||
|
||||
int tmax = Tile.TILE_SIZE + 10;
|
||||
int tmin = -10;
|
||||
|
||||
boolean rounded = false;
|
||||
boolean squared = false;
|
||||
|
||||
if (line.cap == Cap.ROUND)
|
||||
rounded = true;
|
||||
else if (line.cap == Cap.SQUARE)
|
||||
squared = true;
|
||||
|
||||
if (pool == null) {
|
||||
pool = curItem = ShortPool.get();
|
||||
}
|
||||
|
||||
ShortItem si = curItem;
|
||||
short v[] = si.vertices;
|
||||
int opos = si.used;
|
||||
|
||||
for (int i = 0, pos = 0, n = index.length; i < n; i++) {
|
||||
int length = index[i];
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
// save some vertices
|
||||
if (rounded && i > 200)
|
||||
rounded = false;
|
||||
|
||||
int ipos = pos;
|
||||
|
||||
// need at least two points
|
||||
if (length < 4) {
|
||||
pos += length;
|
||||
continue;
|
||||
}
|
||||
// amount of vertices used
|
||||
verticesCnt += length + (rounded ? 6 : 2);
|
||||
|
||||
x = points[ipos++];
|
||||
y = points[ipos++];
|
||||
|
||||
nextX = points[ipos++];
|
||||
nextY = points[ipos++];
|
||||
|
||||
// Calculate triangle corners for the given width
|
||||
vx = nextX - x;
|
||||
vy = nextY - y;
|
||||
|
||||
a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
|
||||
vx = (vx / a);
|
||||
vy = (vy / a);
|
||||
|
||||
ux = -vy;
|
||||
uy = vx;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
short ox, oy, dx, dy;
|
||||
int ddx, ddy;
|
||||
|
||||
ox = (short) (x * COORD_SCALE);
|
||||
oy = (short) (y * COORD_SCALE);
|
||||
|
||||
boolean outside = (x < tmin || x > tmax || y < tmin || y > tmax);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
if (rounded && !outside) {
|
||||
// add first vertex twice
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
// last two bit encode texture coord (-1)
|
||||
dx = (short) (0 | ddx & DIR_MASK);
|
||||
dy = (short) (2 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (2 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// Start of line
|
||||
ddx = (int) (ux * DIR_SCALE);
|
||||
ddy = (int) (uy * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | -ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | -ddy & DIR_MASK);
|
||||
|
||||
} else {
|
||||
// outside means line is probably clipped
|
||||
// TODO should align ending with tile boundary
|
||||
// for now, just extend the line a little
|
||||
|
||||
if (squared) {
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
} else if (!outside) {
|
||||
vx *= 0.5;
|
||||
vy *= 0.5;
|
||||
}
|
||||
|
||||
if (rounded)
|
||||
verticesCnt -= 2;
|
||||
|
||||
// add first vertex twice
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
dx = (short) (0 | ddx & DIR_MASK);
|
||||
dy = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
}
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
x = nextX;
|
||||
y = nextY;
|
||||
|
||||
for (; ipos < pos + length;) {
|
||||
nextX = points[ipos++];
|
||||
nextY = points[ipos++];
|
||||
|
||||
// Unit vector pointing back to previous node
|
||||
vx = prevX - x;
|
||||
vy = prevY - y;
|
||||
a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
vx = (vx / a);
|
||||
vy = (vy / a);
|
||||
|
||||
// Unit vector pointing forward to next node
|
||||
wx = nextX - x;
|
||||
wy = nextY - y;
|
||||
a = FloatMath.sqrt(wx * wx + wy * wy);
|
||||
wx = (wx / a);
|
||||
wy = (wy / a);
|
||||
|
||||
// Sum of these two vectors points
|
||||
ux = vx + wx;
|
||||
uy = vy + wy;
|
||||
|
||||
a = -wy * ux + wx * uy;
|
||||
|
||||
// boolean split = false;
|
||||
if (a < 0.01f && a > -0.01f) {
|
||||
// Almost straight
|
||||
ux = -wy;
|
||||
uy = wx;
|
||||
} else {
|
||||
ux = (ux / a);
|
||||
uy = (uy / a);
|
||||
|
||||
// hack to avoid miter going to infinity
|
||||
if (ux > 2.0f || ux < -2.0f || uy > 2.0f || uy < -2.0f) {
|
||||
ux = -wy;
|
||||
uy = wx;
|
||||
}
|
||||
}
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
ox = (short) (x * COORD_SCALE);
|
||||
oy = (short) (y * COORD_SCALE);
|
||||
|
||||
ddx = (int) (ux * DIR_SCALE);
|
||||
ddy = (int) (uy * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | -ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | -ddy & DIR_MASK);
|
||||
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
x = nextX;
|
||||
y = nextY;
|
||||
}
|
||||
|
||||
vx = prevX - x;
|
||||
vy = prevY - y;
|
||||
|
||||
a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
|
||||
vx = (vx / a);
|
||||
vy = (vy / a);
|
||||
|
||||
ux = vy;
|
||||
uy = -vx;
|
||||
|
||||
outside = (x < tmin || x > tmax || y < tmin || y > tmax);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si.next = ShortPool.get();
|
||||
si = si.next;
|
||||
opos = 0;
|
||||
v = si.vertices;
|
||||
}
|
||||
|
||||
ox = (short) (x * COORD_SCALE);
|
||||
oy = (short) (y * COORD_SCALE);
|
||||
|
||||
if (rounded && !outside) {
|
||||
ddx = (int) (ux * DIR_SCALE);
|
||||
ddy = (int) (uy * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (2 | -ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | -ddy & DIR_MASK);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// For rounded line edges
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
dx = (short) (0 | ddx & DIR_MASK);
|
||||
dy = (short) (0 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// add last vertex twice
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
dx = (short) (2 | ddx & DIR_MASK);
|
||||
dy = (short) (0 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
} else {
|
||||
if (squared) {
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
} else if (!outside) {
|
||||
vx *= 0.5;
|
||||
vy *= 0.5;
|
||||
}
|
||||
|
||||
if (rounded)
|
||||
verticesCnt -= 2;
|
||||
|
||||
ddx = (int) ((ux - vx) * DIR_SCALE);
|
||||
ddy = (int) ((uy - vy) * DIR_SCALE);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = (short) (0 | ddx & DIR_MASK);
|
||||
v[opos++] = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
// add last vertex twice
|
||||
ddx = (int) (-(ux + vx) * DIR_SCALE);
|
||||
ddy = (int) (-(uy + vy) * DIR_SCALE);
|
||||
dx = (short) (2 | ddx & DIR_MASK);
|
||||
dy = (short) (1 | ddy & DIR_MASK);
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
|
||||
if (opos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
opos = 0;
|
||||
}
|
||||
|
||||
v[opos++] = ox;
|
||||
v[opos++] = oy;
|
||||
v[opos++] = dx;
|
||||
v[opos++] = dy;
|
||||
}
|
||||
pos += length;
|
||||
}
|
||||
|
||||
si.used = opos;
|
||||
curItem = si;
|
||||
}
|
||||
}
|
||||
320
src/org/oscim/view/glrenderer/LineLayers.java
Normal file
320
src/org/oscim/view/glrenderer/LineLayers.java
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
|
||||
import static android.opengl.GLES20.glDisableVertexAttribArray;
|
||||
import static android.opengl.GLES20.glDrawArrays;
|
||||
import static android.opengl.GLES20.glEnableVertexAttribArray;
|
||||
import static android.opengl.GLES20.glGetAttribLocation;
|
||||
import static android.opengl.GLES20.glGetUniformLocation;
|
||||
import static android.opengl.GLES20.glUniform4f;
|
||||
import static android.opengl.GLES20.glUniform4fv;
|
||||
import static android.opengl.GLES20.glUniformMatrix4fv;
|
||||
import static android.opengl.GLES20.glUseProgram;
|
||||
import static android.opengl.GLES20.glVertexAttribPointer;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.view.utils.GlUtils;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.util.FloatMath;
|
||||
|
||||
class LineLayers {
|
||||
private static int NUM_VERTEX_SHORTS = 4;
|
||||
|
||||
private static final int LINE_VERTICES_DATA_POS_OFFSET = 0;
|
||||
private static final int LINE_VERTICES_DATA_TEX_OFFSET = 4;
|
||||
|
||||
// shader handles
|
||||
private static int lineProgram;
|
||||
private static int hLineVertexPosition;
|
||||
private static int hLineTexturePosition;
|
||||
private static int hLineColor;
|
||||
private static int hLineMatrix;
|
||||
private static int hLineScale;
|
||||
private static int hLineWidth;
|
||||
|
||||
static boolean init() {
|
||||
lineProgram = GlUtils.createProgram(Shaders.lineVertexShader,
|
||||
Shaders.lineFragmentShader);
|
||||
if (lineProgram == 0) {
|
||||
// Log.e(TAG, "Could not create line program.");
|
||||
return false;
|
||||
}
|
||||
|
||||
hLineMatrix = glGetUniformLocation(lineProgram, "mvp");
|
||||
hLineScale = glGetUniformLocation(lineProgram, "u_wscale");
|
||||
hLineWidth = glGetUniformLocation(lineProgram, "u_width");
|
||||
hLineColor = glGetUniformLocation(lineProgram, "u_color");
|
||||
hLineVertexPosition = GLES20.glGetAttribLocation(lineProgram, "a_position");
|
||||
hLineTexturePosition = glGetAttribLocation(lineProgram, "a_st");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static final boolean mFast = true;
|
||||
|
||||
static LineLayer drawLines(MapTile tile, LineLayer layer, int next, float[] matrix,
|
||||
float div, double zoom, float scale) {
|
||||
|
||||
float z = 1 / div;
|
||||
|
||||
if (layer == null)
|
||||
return null;
|
||||
|
||||
glUseProgram(lineProgram);
|
||||
|
||||
glEnableVertexAttribArray(hLineVertexPosition);
|
||||
glEnableVertexAttribArray(hLineTexturePosition);
|
||||
|
||||
glVertexAttribPointer(hLineVertexPosition, 2, GLES20.GL_SHORT,
|
||||
false, 8, tile.lineOffset + LINE_VERTICES_DATA_POS_OFFSET);
|
||||
|
||||
glVertexAttribPointer(hLineTexturePosition, 2, GLES20.GL_SHORT,
|
||||
false, 8, tile.lineOffset + LINE_VERTICES_DATA_TEX_OFFSET);
|
||||
|
||||
glUniformMatrix4fv(hLineMatrix, 1, false, matrix, 0);
|
||||
|
||||
// scale factor to map one pixel on tile to one pixel on screen:
|
||||
float pixel = 2.0f / (scale * z);
|
||||
|
||||
if (mFast)
|
||||
GLES20.glUniform1f(hLineScale, pixel);
|
||||
else
|
||||
GLES20.glUniform1f(hLineScale, 0);
|
||||
|
||||
// line scale factor (for non fixed lines)
|
||||
float s = FloatMath.sqrt(scale * z);
|
||||
boolean blur = false;
|
||||
|
||||
LineLayer l = layer;
|
||||
for (; l != null && l.layer < next; l = l.next) {
|
||||
Line line = l.line;
|
||||
if (line.fade != -1 && line.fade > zoom)
|
||||
continue;
|
||||
|
||||
if (line.fade >= zoom) {
|
||||
float alpha = 1.0f;
|
||||
|
||||
alpha = (scale > 1.2f ? scale : 1.2f) - alpha;
|
||||
if (alpha > 1.0f)
|
||||
alpha = 1.0f;
|
||||
glUniform4f(hLineColor,
|
||||
line.color[0] * alpha,
|
||||
line.color[1] * alpha,
|
||||
line.color[2] * alpha,
|
||||
alpha);
|
||||
} else {
|
||||
glUniform4fv(hLineColor, 1, line.color, 0);
|
||||
}
|
||||
|
||||
if (blur) {
|
||||
if (mFast)
|
||||
GLES20.glUniform1f(hLineScale, pixel);
|
||||
else
|
||||
GLES20.glUniform1f(hLineScale, 0);
|
||||
blur = false;
|
||||
}
|
||||
|
||||
if (l.isOutline) {
|
||||
for (LineLayer o = l.outlines; o != null; o = o.outlines) {
|
||||
|
||||
if (line.blur != 0) {
|
||||
GLES20.glUniform1f(hLineScale, (l.width + o.width) / (scale * z)
|
||||
- (line.blur / (scale * z)));
|
||||
blur = true;
|
||||
}
|
||||
|
||||
if (zoom > MapGenerator.STROKE_MAX_ZOOM_LEVEL)
|
||||
GLES20.glUniform1f(hLineWidth,
|
||||
(l.width + o.width) / (scale * z));
|
||||
else
|
||||
GLES20.glUniform1f(hLineWidth, l.width / (scale * z)
|
||||
+ o.width / s);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (line.blur != 0) {
|
||||
GLES20.glUniform1f(hLineScale, (l.width / s) * line.blur);
|
||||
blur = true;
|
||||
}
|
||||
|
||||
if (line.fixed || zoom > MapGenerator.STROKE_MAX_ZOOM_LEVEL) {
|
||||
// invert scaling of extrusion vectors so that line width stays the same
|
||||
GLES20.glUniform1f(hLineWidth, (l.width / (scale * z)));
|
||||
} else {
|
||||
GLES20.glUniform1f(hLineWidth, (l.width / s));
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);
|
||||
}
|
||||
}
|
||||
|
||||
glDisableVertexAttribArray(hLineVertexPosition);
|
||||
glDisableVertexAttribArray(hLineTexturePosition);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int sizeOf(LineLayer layers) {
|
||||
int size = 0;
|
||||
for (LineLayer l = layers; l != null; l = l.next)
|
||||
size += l.verticesCnt;
|
||||
|
||||
size *= NUM_VERTEX_SHORTS;
|
||||
return size;
|
||||
}
|
||||
|
||||
static void compileLayerData(LineLayer layers, ShortBuffer sbuf) {
|
||||
int pos = 0;
|
||||
ShortItem last = null, items = null;
|
||||
|
||||
for (LineLayer l = layers; l != null; l = l.next) {
|
||||
if (l.isOutline)
|
||||
continue;
|
||||
|
||||
for (ShortItem item = l.pool; item != null; item = item.next) {
|
||||
|
||||
if (item.next == null) {
|
||||
sbuf.put(item.vertices, 0, item.used);
|
||||
} else {
|
||||
// item.used = ShortItem.SIZE;
|
||||
sbuf.put(item.vertices);
|
||||
}
|
||||
|
||||
last = item;
|
||||
}
|
||||
|
||||
l.offset = pos;
|
||||
pos += l.verticesCnt;
|
||||
|
||||
if (last != null) {
|
||||
last.next = items;
|
||||
items = l.pool;
|
||||
}
|
||||
|
||||
l.pool = null;
|
||||
l.curItem = null;
|
||||
}
|
||||
|
||||
ShortPool.add(items);
|
||||
}
|
||||
|
||||
// @SuppressLint("UseValueOf")
|
||||
// private static final Boolean lock = new Boolean(true);
|
||||
// private static final int POOL_LIMIT = 1500;
|
||||
//
|
||||
// static private LineLayer pool = null;
|
||||
// static private int count = 0;
|
||||
// static private int countAll = 0;
|
||||
//
|
||||
// static void finish() {
|
||||
// synchronized (lock) {
|
||||
// count = 0;
|
||||
// countAll = 0;
|
||||
// pool = null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static LineLayer get(int layer, Line line, float width, boolean outline) {
|
||||
// synchronized (lock) {
|
||||
//
|
||||
// if (count == 0 && pool == null) {
|
||||
// countAll++;
|
||||
// return new LineLayer(layer, line, width, outline);
|
||||
// }
|
||||
// if (count > 0) {
|
||||
// count--;
|
||||
// } else {
|
||||
// int c = 0;
|
||||
// LineLayer tmp = pool;
|
||||
//
|
||||
// while (tmp != null) {
|
||||
// c++;
|
||||
// tmp = tmp.next;
|
||||
// }
|
||||
//
|
||||
// Log.d("LineLayersl", "eek wrong count: " + c + " left");
|
||||
// }
|
||||
//
|
||||
// LineLayer it = pool;
|
||||
// pool = pool.next;
|
||||
// it.next = null;
|
||||
// it.layer = layer;
|
||||
// it.line = line;
|
||||
// it.isOutline = outline;
|
||||
// it.width = width;
|
||||
// return it;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static void add(LineLayer layers) {
|
||||
// if (layers == null)
|
||||
// return;
|
||||
//
|
||||
// synchronized (lock) {
|
||||
//
|
||||
// // limit pool items
|
||||
// if (countAll < POOL_LIMIT) {
|
||||
// LineLayer last = layers;
|
||||
//
|
||||
// while (true) {
|
||||
// count++;
|
||||
//
|
||||
// if (last.next == null)
|
||||
// break;
|
||||
//
|
||||
// last = last.next;
|
||||
// }
|
||||
//
|
||||
// last.next = pool;
|
||||
// pool = layers;
|
||||
//
|
||||
// } else {
|
||||
// int cleared = 0;
|
||||
// LineLayer prev, tmp = layers;
|
||||
// while (tmp != null) {
|
||||
// prev = tmp;
|
||||
// tmp = tmp.next;
|
||||
//
|
||||
// countAll--;
|
||||
// cleared++;
|
||||
//
|
||||
// prev.next = null;
|
||||
//
|
||||
// }
|
||||
// Log.d("LineLayers", "sum: " + countAll + " free: " + count + " freed "
|
||||
// + cleared);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
static void clear(LineLayer layer) {
|
||||
for (LineLayer l = layer; l != null; l = l.next) {
|
||||
if (l.pool != null) {
|
||||
ShortPool.add(l.pool);
|
||||
l.pool = null;
|
||||
l.curItem = null;
|
||||
}
|
||||
}
|
||||
// LineLayers.add(layer);
|
||||
}
|
||||
}
|
||||
617
src/org/oscim/view/glrenderer/MapGenerator.java
Normal file
617
src/org/oscim/view/glrenderer/MapGenerator.java
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Tag;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.core.WebMercator;
|
||||
import org.oscim.database.IMapDatabase;
|
||||
import org.oscim.database.IMapDatabaseCallback;
|
||||
import org.oscim.database.QueryResult;
|
||||
import org.oscim.theme.IRenderCallback;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.renderinstruction.Area;
|
||||
import org.oscim.theme.renderinstruction.Caption;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
import org.oscim.theme.renderinstruction.RenderInstruction;
|
||||
import org.oscim.view.DebugSettings;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabaseCallback {
|
||||
|
||||
private static String TAG = MapGenerator.class.getName();
|
||||
|
||||
private static final double PI180 = (Math.PI / 180) / 1000000.0;
|
||||
private static final double PIx4 = Math.PI * 4;
|
||||
private static final double STROKE_INCREASE = Math.sqrt(2);
|
||||
private static final byte LAYERS = 11;
|
||||
private static final double f900913 = 20037508.342789244;
|
||||
|
||||
static final byte STROKE_MIN_ZOOM_LEVEL = 12;
|
||||
static final byte STROKE_MAX_ZOOM_LEVEL = 17;
|
||||
|
||||
private static RenderTheme renderTheme;
|
||||
|
||||
private IMapDatabase mMapDatabase;
|
||||
|
||||
private MapTile mCurrentTile;
|
||||
|
||||
private float[] mWayNodes;
|
||||
private short[] mWays;
|
||||
|
||||
private LineLayer mLineLayers;
|
||||
private PolygonLayer mPolyLayers;
|
||||
private LineLayer mCurLineLayer;
|
||||
private PolygonLayer mCurPolyLayer;
|
||||
|
||||
private TextItem mLabels;
|
||||
|
||||
private int mDrawingLayer;
|
||||
private int mLevels;
|
||||
|
||||
private float mStrokeScale = 1.0f;
|
||||
|
||||
private boolean mProjected;
|
||||
// private boolean mProjectedResult;
|
||||
private float mSimplify;
|
||||
// private boolean firstMatch;
|
||||
// private boolean prevClosed;
|
||||
|
||||
private RenderInstruction[] mRenderInstructions = null;
|
||||
|
||||
private final String TAG_WATER = "water".intern();
|
||||
private final MapView mMapView;
|
||||
|
||||
private final Tag[] debugTagBox = { new Tag("debug", "box") };
|
||||
private final Tag[] debugTagWay = { new Tag("debug", "way") };
|
||||
private final Tag[] debugTagArea = { new Tag("debug", "area") };
|
||||
private final float[] debugBoxCoords = { 0, 0, 0, Tile.TILE_SIZE,
|
||||
Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0, 0, 0 };
|
||||
private final short[] debugBoxIndex = { 10 };
|
||||
|
||||
private float mProjectionScaleFactor;
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public MapGenerator(MapView mapView) {
|
||||
Log.d(TAG, "init DatabaseRenderer");
|
||||
mMapView = mapView;
|
||||
}
|
||||
|
||||
private float mPoiX = 256;
|
||||
private float mPoiY = 256;
|
||||
|
||||
private Tag mTagEmptyName = new Tag(Tag.TAG_KEY_NAME, null, false);
|
||||
private Tag mTagName;
|
||||
|
||||
private void filterTags(Tag[] tags) {
|
||||
for (int i = 0; i < tags.length; i++) {
|
||||
// Log.d(TAG, "check tag: " + tags[i]);
|
||||
if (tags[i].key == Tag.TAG_KEY_NAME && tags[i].value != null) {
|
||||
mTagName = tags[i];
|
||||
tags[i] = mTagEmptyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private RenderInstruction[] mNodeRenderInstructions;
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterest(byte layer, float latitude, float longitude,
|
||||
Tag[] tags) {
|
||||
|
||||
mTagName = null;
|
||||
|
||||
if (mMapProjection != null)
|
||||
{
|
||||
long x = mCurrentTile.pixelX;
|
||||
long y = mCurrentTile.pixelY + Tile.TILE_SIZE;
|
||||
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
|
||||
|
||||
double divx, divy;
|
||||
long dx = (x - (z >> 1));
|
||||
long dy = (y - (z >> 1));
|
||||
|
||||
if (mMapProjection == WebMercator.NAME) {
|
||||
double div = f900913 / (z >> 1);
|
||||
// divy = f900913 / (z >> 1);
|
||||
mPoiX = (float) (longitude / div - dx);
|
||||
mPoiY = (float) (latitude / div + dy);
|
||||
} else {
|
||||
divx = 180000000.0 / (z >> 1);
|
||||
divy = z / PIx4;
|
||||
mPoiX = (float) (longitude / divx - dx);
|
||||
double sinLat = Math.sin(latitude * PI180);
|
||||
mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
|
||||
if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10
|
||||
|| mPoiY > Tile.TILE_SIZE + 10)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mPoiX = longitude;
|
||||
mPoiY = latitude;
|
||||
}
|
||||
|
||||
// remove tags that should not be cached in Rendertheme
|
||||
filterTags(tags);
|
||||
// Log.d(TAG, "renderPointOfInterest: " + mTagName);
|
||||
|
||||
// mNodeRenderInstructions =
|
||||
MapGenerator.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaterBackground() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWay(byte layer, Tag[] tags, float[] wayNodes, short[] wayLength,
|
||||
boolean closed) {
|
||||
|
||||
mTagName = null;
|
||||
mProjected = false;
|
||||
mCurLineLayer = null;
|
||||
|
||||
mDrawingLayer = getValidLayer(layer) * mLevels;
|
||||
mSimplify = 0.5f;
|
||||
|
||||
if (closed) {
|
||||
if (mCurrentTile.zoomLevel < 14)
|
||||
mSimplify = 0.5f;
|
||||
else
|
||||
mSimplify = 0.2f;
|
||||
|
||||
if (tags.length == 1 && TAG_WATER == (tags[0].value))
|
||||
mSimplify = 0;
|
||||
}
|
||||
|
||||
mWayNodes = wayNodes;
|
||||
mWays = wayLength;
|
||||
|
||||
// remove tags that should not be cached in Rendertheme
|
||||
filterTags(tags);
|
||||
|
||||
// if (mRenderInstructions != null) {
|
||||
// for (int i = 0, n = mRenderInstructions.length; i < n; i++)
|
||||
// mRenderInstructions[i].renderWay(this, tags);
|
||||
// }
|
||||
|
||||
mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags,
|
||||
(byte) (mCurrentTile.zoomLevel + 0),
|
||||
closed, true);
|
||||
|
||||
if (mRenderInstructions == null && mDebugDrawUnmatched)
|
||||
debugUnmatched(closed, tags);
|
||||
}
|
||||
|
||||
private void debugUnmatched(boolean closed, Tag[] tags) {
|
||||
|
||||
Log.d(TAG, "way not matched: " + tags[0] + " "
|
||||
+ (tags.length > 1 ? tags[1] : "") + " " + closed);
|
||||
|
||||
mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false);
|
||||
|
||||
if (closed) {
|
||||
mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagArea,
|
||||
(byte) 0, true, true);
|
||||
} else {
|
||||
mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagWay,
|
||||
(byte) 0, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaCaption(Caption caption) {
|
||||
// Log.d(TAG, "renderAreaCaption: " + mTagName);
|
||||
|
||||
if (mTagName == null)
|
||||
return;
|
||||
|
||||
if (caption.textKey == mTagEmptyName.key) {
|
||||
|
||||
TextItem t = new TextItem(mWayNodes[0], mWayNodes[1], mTagName.value, caption);
|
||||
t.next = mLabels;
|
||||
mLabels = t;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCaption(Caption caption) {
|
||||
// Log.d(TAG, "renderPointOfInterestCaption: " + mPoiX + " " + mPoiY + " "
|
||||
// + mTagName);
|
||||
|
||||
if (mTagName == null)
|
||||
return;
|
||||
|
||||
if (caption.textKey == mTagEmptyName.key) {
|
||||
TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, caption);
|
||||
t.next = mLabels;
|
||||
mLabels = t;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWayText(PathText pathText) {
|
||||
// Log.d(TAG, "renderWayText: " + mTagName);
|
||||
|
||||
if (mTagName == null)
|
||||
return;
|
||||
|
||||
if (pathText.textKey == mTagEmptyName.key) {
|
||||
|
||||
mLabels = WayDecorator.renderText(mWayNodes, mTagName.value, pathText, 0,
|
||||
mWays[0], mLabels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaSymbol(Bitmap symbol) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCircle(float radius, Paint fill, int level) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestSymbol(Bitmap symbol) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
private int countLines;
|
||||
private int countNodes;
|
||||
|
||||
@Override
|
||||
public void renderWay(Line line, int level) {
|
||||
|
||||
projectToTile();
|
||||
|
||||
if (line.outline && mCurLineLayer == null)
|
||||
return;
|
||||
|
||||
float w = line.width;
|
||||
|
||||
if (!line.fixed) {
|
||||
w *= mStrokeScale;
|
||||
w *= mProjectionScaleFactor;
|
||||
}
|
||||
|
||||
LineLayer lineLayer = null;
|
||||
|
||||
int numLayer = mDrawingLayer + level;
|
||||
|
||||
LineLayer l = mLineLayers;
|
||||
|
||||
if (mCurLineLayer != null && mCurLineLayer.layer == numLayer) {
|
||||
lineLayer = mCurLineLayer;
|
||||
} else if (l == null || l.layer > numLayer) {
|
||||
// insert new layer at start
|
||||
lineLayer = new LineLayer(numLayer, line, w, line.outline);
|
||||
// lineLayer = LineLayers.get(numLayer, line, w, false);
|
||||
|
||||
lineLayer.next = l;
|
||||
mLineLayers = lineLayer;
|
||||
} else {
|
||||
while (l != null) {
|
||||
// found layer
|
||||
if (l.layer == numLayer) {
|
||||
lineLayer = l;
|
||||
break;
|
||||
}
|
||||
// insert new layer between current and next layer
|
||||
if (l.next == null || l.next.layer > numLayer) {
|
||||
lineLayer = new LineLayer(numLayer, line, w, line.outline);
|
||||
// lineLayer = LineLayers.get(numLayer, line, w, false);
|
||||
lineLayer.next = l.next;
|
||||
l.next = lineLayer;
|
||||
}
|
||||
l = l.next;
|
||||
}
|
||||
}
|
||||
|
||||
if (lineLayer == null) {
|
||||
mCurLineLayer = null;
|
||||
return;
|
||||
}
|
||||
if (line.outline) {
|
||||
lineLayer.addOutline(mCurLineLayer);
|
||||
return;
|
||||
}
|
||||
|
||||
mCurLineLayer = lineLayer;
|
||||
|
||||
lineLayer.addLine(mWayNodes, mWays);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderArea(Area area, int level) {
|
||||
if (!mDebugDrawPolygons)
|
||||
return;
|
||||
|
||||
if (!mProjected && !projectToTile())
|
||||
return;
|
||||
|
||||
int numLayer = mDrawingLayer + level;
|
||||
|
||||
PolygonLayer layer = null;
|
||||
PolygonLayer l = mPolyLayers;
|
||||
|
||||
if (mCurPolyLayer != null && mCurPolyLayer.layer == numLayer) {
|
||||
layer = mCurPolyLayer;
|
||||
} else if (l == null || l.layer > numLayer) {
|
||||
// insert new layer at start
|
||||
layer = new PolygonLayer(numLayer, area);
|
||||
layer.next = l;
|
||||
mPolyLayers = layer;
|
||||
} else {
|
||||
while (l != null) {
|
||||
|
||||
if (l.layer == numLayer) {
|
||||
layer = l;
|
||||
break;
|
||||
}
|
||||
|
||||
// insert new layer between current and next layer
|
||||
if (l.next == null || l.next.layer > numLayer) {
|
||||
layer = new PolygonLayer(numLayer, area);
|
||||
layer.next = l.next;
|
||||
l.next = layer;
|
||||
}
|
||||
l = l.next;
|
||||
}
|
||||
}
|
||||
if (layer == null)
|
||||
return;
|
||||
|
||||
mCurPolyLayer = layer;
|
||||
|
||||
layer.addPolygon(mWayNodes, mWays);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaySymbol(Bitmap symbol, boolean alignCenter, boolean repeat) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
private boolean mDebugDrawPolygons;
|
||||
boolean mDebugDrawUnmatched;
|
||||
|
||||
@Override
|
||||
public boolean executeJob(JobTile jobTile) {
|
||||
MapTile tile;
|
||||
|
||||
if (mMapDatabase == null)
|
||||
return false;
|
||||
|
||||
tile = mCurrentTile = (MapTile) jobTile;
|
||||
DebugSettings debugSettings = mMapView.getDebugSettings();
|
||||
|
||||
mDebugDrawPolygons = !debugSettings.mDisablePolygons;
|
||||
mDebugDrawUnmatched = debugSettings.mDrawUnmatchted;
|
||||
|
||||
// fixed now....
|
||||
if (tile.newData || tile.isReady) {
|
||||
Log.d(TAG, "XXX tile already loaded "
|
||||
+ tile + " "
|
||||
+ tile.newData + " "
|
||||
+ tile.isReady + " ");
|
||||
return false;
|
||||
}
|
||||
|
||||
mLevels = MapGenerator.renderTheme.getLevels();
|
||||
|
||||
// limit stroke scale at z=17
|
||||
if (tile.zoomLevel < STROKE_MAX_ZOOM_LEVEL)
|
||||
setScaleStrokeWidth(tile.zoomLevel);
|
||||
else
|
||||
setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL);
|
||||
|
||||
// firstMatch = true;
|
||||
countLines = 0;
|
||||
countNodes = 0;
|
||||
|
||||
// acount for area changes with latitude
|
||||
mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection
|
||||
.pixelYToLatitude(tile.pixelY, tile.zoomLevel)
|
||||
* (Math.PI / 180)));
|
||||
|
||||
if (mMapDatabase.executeQuery(tile, this) != QueryResult.SUCCESS) {
|
||||
Log.d(TAG, "Failed loading: " + tile);
|
||||
LineLayers.clear(mLineLayers);
|
||||
PolygonLayers.clear(mPolyLayers);
|
||||
mLineLayers = null;
|
||||
mPolyLayers = null;
|
||||
mLabels = null;
|
||||
tile.isLoading = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debugSettings.mDrawTileFrames) {
|
||||
mTagName = new Tag("name", countLines + " " + countNodes + " "
|
||||
+ tile.toString(), false);
|
||||
mPoiX = Tile.TILE_SIZE >> 1;
|
||||
mPoiY = 10;
|
||||
MapGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
|
||||
|
||||
mWays = debugBoxIndex;
|
||||
mWayNodes = debugBoxCoords;
|
||||
mDrawingLayer = 10 * mLevels;
|
||||
MapGenerator.renderTheme.matchWay(this, debugTagBox, (byte) 0, false, true);
|
||||
}
|
||||
|
||||
tile.lineLayers = mLineLayers;
|
||||
tile.polygonLayers = mPolyLayers;
|
||||
tile.labels = mLabels;
|
||||
|
||||
mCurPolyLayer = null;
|
||||
mCurLineLayer = null;
|
||||
mLineLayers = null;
|
||||
mPolyLayers = null;
|
||||
mLabels = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte getValidLayer(byte layer) {
|
||||
if (layer < 0) {
|
||||
return 0;
|
||||
/**
|
||||
* return instances of MapRenderer
|
||||
*/
|
||||
} else if (layer >= LAYERS) {
|
||||
return LAYERS - 1;
|
||||
} else {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 void setScaleStrokeWidth(byte zoomLevel) {
|
||||
int zoomLevelDiff = Math.max(zoomLevel - STROKE_MIN_ZOOM_LEVEL, 0);
|
||||
mStrokeScale = (float) Math.pow(STROKE_INCREASE, zoomLevelDiff);
|
||||
if (mStrokeScale < 1)
|
||||
mStrokeScale = 1;
|
||||
}
|
||||
|
||||
private String mMapProjection;
|
||||
|
||||
@Override
|
||||
public void setMapDatabase(IMapDatabase mapDatabase) {
|
||||
mMapDatabase = mapDatabase;
|
||||
mMapProjection = mMapDatabase.getMapProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMapDatabase getMapDatabase() {
|
||||
return mMapDatabase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderTheme(RenderTheme theme) {
|
||||
MapGenerator.renderTheme = theme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkWay(Tag[] tags, boolean closed) {
|
||||
|
||||
mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags,
|
||||
(byte) (mCurrentTile.zoomLevel + 0), closed, false);
|
||||
|
||||
return mRenderInstructions != null;
|
||||
}
|
||||
|
||||
private boolean projectToTile() {
|
||||
if (mProjected || mMapProjection == null)
|
||||
return true;
|
||||
|
||||
boolean useWebMercator = false;
|
||||
|
||||
if (mMapProjection == WebMercator.NAME)
|
||||
useWebMercator = true;
|
||||
|
||||
float[] coords = mWayNodes;
|
||||
|
||||
long x = mCurrentTile.pixelX;
|
||||
long y = mCurrentTile.pixelY + Tile.TILE_SIZE;
|
||||
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
|
||||
float min = mSimplify;
|
||||
|
||||
double divx, divy;
|
||||
long dx = (x - (z >> 1));
|
||||
long dy = (y - (z >> 1));
|
||||
|
||||
if (useWebMercator) {
|
||||
divx = f900913 / (z >> 1);
|
||||
divy = f900913 / (z >> 1);
|
||||
} else {
|
||||
divx = 180000000.0 / (z >> 1);
|
||||
divy = z / PIx4;
|
||||
}
|
||||
|
||||
for (int pos = 0, outPos = 0, i = 0, m = mWays.length; i < m; i++) {
|
||||
int len = mWays[i];
|
||||
if (len == 0)
|
||||
continue;
|
||||
if (len < 0)
|
||||
break;
|
||||
|
||||
int cnt = 0;
|
||||
float lat, lon, prevLon = 0, prevLat = 0;
|
||||
|
||||
for (int end = pos + len; pos < end; pos += 2) {
|
||||
|
||||
if (useWebMercator) {
|
||||
lon = (float) (coords[pos] / divx - dx);
|
||||
lat = (float) (coords[pos + 1] / divy + dy);
|
||||
} else {
|
||||
lon = (float) ((coords[pos]) / divx - dx);
|
||||
double sinLat = Math.sin(coords[pos + 1] * PI180);
|
||||
lat = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
|
||||
}
|
||||
|
||||
if (cnt != 0) {
|
||||
// drop small distance intermediate nodes
|
||||
if (lat == prevLat && lon == prevLon)
|
||||
continue;
|
||||
|
||||
if ((pos != end - 2) &&
|
||||
!((lat > prevLat + min || lat < prevLat - min) ||
|
||||
(lon > prevLon + min || lon < prevLon - min)))
|
||||
continue;
|
||||
}
|
||||
coords[outPos++] = prevLon = lon;
|
||||
coords[outPos++] = prevLat = lat;
|
||||
|
||||
cnt += 2;
|
||||
}
|
||||
|
||||
mWays[i] = (short) cnt;
|
||||
}
|
||||
mProjected = true;
|
||||
// mProjectedResult = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
823
src/org/oscim/view/glrenderer/MapRenderer.java
Normal file
823
src/org/oscim/view/glrenderer/MapRenderer.java
Normal file
@@ -0,0 +1,823 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import static android.opengl.GLES20.GL_ARRAY_BUFFER;
|
||||
import static android.opengl.GLES20.GL_BLEND;
|
||||
import static android.opengl.GLES20.GL_DYNAMIC_DRAW;
|
||||
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
|
||||
import static android.opengl.GLES20.GL_STENCIL_BUFFER_BIT;
|
||||
import static android.opengl.GLES20.glBindBuffer;
|
||||
import static android.opengl.GLES20.glBlendFunc;
|
||||
import static android.opengl.GLES20.glBufferData;
|
||||
import static android.opengl.GLES20.glClear;
|
||||
import static android.opengl.GLES20.glClearColor;
|
||||
import static android.opengl.GLES20.glClearStencil;
|
||||
import static android.opengl.GLES20.glDisable;
|
||||
import static android.opengl.GLES20.glEnable;
|
||||
import static android.opengl.GLES20.glGenBuffers;
|
||||
import static android.opengl.GLES20.glViewport;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
import org.oscim.view.utils.GlUtils;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.SystemClock;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
public class MapRenderer implements org.oscim.view.IMapRenderer {
|
||||
|
||||
private static final String TAG = "MapRenderer";
|
||||
|
||||
private static final int MB = 1024 * 1024;
|
||||
private static final int SHORT_BYTES = 2;
|
||||
static final float COORD_MULTIPLIER = 8.0f;
|
||||
|
||||
private static final int CACHE_TILES_MAX = 200;
|
||||
private static final int LIMIT_BUFFERS = 16 * MB;
|
||||
|
||||
static int CACHE_TILES = CACHE_TILES_MAX;
|
||||
|
||||
private final MapView mMapView;
|
||||
private static ArrayList<VertexBufferObject> mVBOs;
|
||||
|
||||
private static int mWidth, mHeight;
|
||||
private static float mAspect;
|
||||
|
||||
private static int rotateBuffers = 2;
|
||||
private static ShortBuffer shortBuffer[];
|
||||
private static short[] mFillCoords;
|
||||
|
||||
// bytes currently loaded in VBOs
|
||||
private static int mBufferMemoryUsage;
|
||||
|
||||
private static float[] mMVPMatrix = new float[16];
|
||||
private static float[] mRotateMatrix = new float[16];
|
||||
private static float[] mProjMatrix = new float[16];
|
||||
private static float[] mRotTMatrix = new float[16];
|
||||
|
||||
// curTiles is set by TileLoader and swapped with
|
||||
// drawTiles in onDrawFrame in GL thread.
|
||||
private static TilesData curTiles, drawTiles;
|
||||
|
||||
// flag set by updateVisibleList when current visible tiles
|
||||
// changed. used in onDrawFrame to flip curTiles/drawTiles
|
||||
private static boolean mUpdateTiles;
|
||||
|
||||
private static MapPosition mCurPosition;
|
||||
|
||||
private float[] mClearColor = null;
|
||||
|
||||
// number of tiles drawn in one frame
|
||||
private static short mDrawCount = 0;
|
||||
|
||||
private static boolean mUpdateColor = false;
|
||||
|
||||
// lock to synchronize Main- and GL-Thread
|
||||
static Object lock = new Object();
|
||||
|
||||
// used for passing tiles to be rendered from TileLoader(Main-Thread) to GLThread
|
||||
static class TilesData {
|
||||
int cnt = 0;
|
||||
final MapTile[] tiles;
|
||||
|
||||
TilesData(int numTiles) {
|
||||
tiles = new MapTile[numTiles];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public MapRenderer(MapView mapView) {
|
||||
Log.d(TAG, "init MapRenderer");
|
||||
|
||||
mMapView = mapView;
|
||||
Matrix.setIdentityM(mMVPMatrix, 0);
|
||||
|
||||
mUpdateTiles = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* called by MapView when position or map settings changes
|
||||
*/
|
||||
@Override
|
||||
public void updateMap(boolean clear) {
|
||||
if (mWidth <= 0 || mHeight <= 0)
|
||||
return;
|
||||
|
||||
TileLoader.updateMap(clear);
|
||||
}
|
||||
|
||||
/**
|
||||
* called by TileLoader when only position changed
|
||||
*
|
||||
* @param mapPosition
|
||||
* current MapPosition
|
||||
*/
|
||||
static void updatePosition(MapPosition mapPosition) {
|
||||
mCurPosition = mapPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* called by TileLoader when list of active tiles changed
|
||||
*
|
||||
* @param mapPosition
|
||||
* current MapPosition
|
||||
* @param tiles
|
||||
* active tiles
|
||||
* @return curTiles (the previously active tiles)
|
||||
*/
|
||||
static TilesData updateTiles(MapPosition mapPosition, TilesData tiles) {
|
||||
synchronized (MapRenderer.lock) {
|
||||
|
||||
mCurPosition = mapPosition;
|
||||
|
||||
for (int i = 0; i < curTiles.cnt; i++)
|
||||
curTiles.tiles[i].isActive = false;
|
||||
|
||||
TilesData tmp = curTiles;
|
||||
curTiles = tiles;
|
||||
|
||||
for (int i = 0; i < curTiles.cnt; i++)
|
||||
curTiles.tiles[i].isActive = true;
|
||||
|
||||
for (int j = 0; j < drawTiles.cnt; j++)
|
||||
drawTiles.tiles[j].isActive = true;
|
||||
|
||||
mUpdateTiles = true;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called by TileLoader. when tile is removed from cache, reuse its vbo.
|
||||
*
|
||||
* @param vbo
|
||||
* the VBO
|
||||
*/
|
||||
static void addVBO(VertexBufferObject vbo) {
|
||||
synchronized (mVBOs) {
|
||||
mVBOs.add(vbo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called from MapWorker Thread when tile is loaded by MapGenerator
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean passTile(JobTile jobTile) {
|
||||
MapTile tile = (MapTile) jobTile;
|
||||
|
||||
if (!tile.isLoading) {
|
||||
// no one should be able to use this tile now, mapgenerator passed it,
|
||||
// glthread does nothing until newdata is set.
|
||||
Log.d(TAG, "passTile: canceled " + tile);
|
||||
TileLoader.addTileLoaded(tile);
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized (mVBOs) {
|
||||
int numVBOs = mVBOs.size();
|
||||
|
||||
if (numVBOs > 0 && tile.vbo == null) {
|
||||
tile.vbo = mVBOs.remove(numVBOs - 1);
|
||||
}
|
||||
|
||||
if (tile.vbo == null) {
|
||||
Log.d(TAG, "no VBOs left for " + tile);
|
||||
tile.isLoading = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tile.newData = true;
|
||||
tile.isLoading = false;
|
||||
|
||||
if (!MapView.debugFrameTime)
|
||||
mMapView.requestRender();
|
||||
|
||||
TileLoader.addTileLoaded(tile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// depthRange: -1 to 1, bits: 2^24 => 2/2^24 one step
|
||||
// ... asus has just 16 bit?!
|
||||
// private static final float depthStep = 0.00000011920928955078125f;
|
||||
|
||||
private static boolean mRotate = false;
|
||||
|
||||
private static void setMatrix(MapPosition mapPosition, MapTile tile, float div,
|
||||
int offset) {
|
||||
float x, y, scale;
|
||||
|
||||
if (mRotate) {
|
||||
scale = (float) (1.0 * mapPosition.scale / (mHeight * div));
|
||||
x = (float) (tile.pixelX - mapPosition.x * div);
|
||||
y = (float) (tile.pixelY - mapPosition.y * div);
|
||||
|
||||
Matrix.setIdentityM(mMVPMatrix, 0);
|
||||
|
||||
Matrix.scaleM(mMVPMatrix, 0, scale / COORD_MULTIPLIER,
|
||||
scale / COORD_MULTIPLIER, 1);
|
||||
|
||||
Matrix.translateM(mMVPMatrix, 0,
|
||||
x * COORD_MULTIPLIER,
|
||||
-(y + Tile.TILE_SIZE) * COORD_MULTIPLIER,
|
||||
-1 + offset * 0.01f);
|
||||
|
||||
Matrix.multiplyMM(mMVPMatrix, 0, mRotateMatrix, 0, mMVPMatrix, 0);
|
||||
|
||||
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
|
||||
}
|
||||
else {
|
||||
scale = (float) (2.0 * mapPosition.scale / (mHeight * div));
|
||||
x = (float) (tile.pixelX - mapPosition.x * div);
|
||||
y = (float) (tile.pixelY - mapPosition.y * div);
|
||||
|
||||
mMVPMatrix[12] = x * scale * mAspect;
|
||||
mMVPMatrix[13] = -(y + Tile.TILE_SIZE) * scale;
|
||||
// increase the 'distance' with each tile drawn.
|
||||
mMVPMatrix[14] = -1 + offset * 0.01f; // depthStep; // 0.01f;
|
||||
mMVPMatrix[0] = scale * mAspect / COORD_MULTIPLIER;
|
||||
mMVPMatrix[5] = scale / COORD_MULTIPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean isVisible(MapPosition mapPosition, MapTile tile, float div) {
|
||||
double dx, dy, scale;
|
||||
|
||||
if (div == 0) {
|
||||
dx = tile.pixelX - mapPosition.x;
|
||||
dy = tile.pixelY - mapPosition.y;
|
||||
scale = mapPosition.scale;
|
||||
} else {
|
||||
dx = tile.pixelX - mapPosition.x * div;
|
||||
dy = tile.pixelY - mapPosition.y * div;
|
||||
scale = mapPosition.scale / div;
|
||||
}
|
||||
int size = Tile.TILE_SIZE;
|
||||
int sx = (int) (dx * scale);
|
||||
int sy = (int) (dy * scale);
|
||||
|
||||
// FIXME little hack, need to do scanline check or sth
|
||||
// this kindof works for typical screen aspect
|
||||
if (mRotate) {
|
||||
int ssize = mWidth > mHeight ? mWidth : mHeight;
|
||||
|
||||
if (sy > ssize / 2 || sx > ssize / 2
|
||||
|| sx + size * scale < -ssize / 2
|
||||
|| sy + size * scale < -ssize / 2) {
|
||||
tile.isVisible = false;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (sy > mHeight / 2 || sx > mWidth / 2
|
||||
|| sx + size * scale < -mWidth / 2
|
||||
|| sy + size * scale < -mHeight / 2) {
|
||||
tile.isVisible = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
tile.isVisible = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int uploadCnt = 0;
|
||||
|
||||
private boolean uploadTileData(MapTile tile) {
|
||||
ShortBuffer sbuf = null;
|
||||
|
||||
// use multiple buffers to avoid overwriting buffer while current
|
||||
// data is uploaded (or rather the blocking which is probably done to avoid this)
|
||||
if (uploadCnt >= rotateBuffers) {
|
||||
uploadCnt = 0;
|
||||
GLES20.glFlush();
|
||||
}
|
||||
|
||||
// Upload line data to vertex buffer object
|
||||
synchronized (tile) {
|
||||
if (!tile.newData)
|
||||
return false;
|
||||
|
||||
int lineSize = LineLayers.sizeOf(tile.lineLayers);
|
||||
int polySize = PolygonLayers.sizeOf(tile.polygonLayers);
|
||||
int newSize = lineSize + polySize;
|
||||
|
||||
if (newSize == 0) {
|
||||
LineLayers.clear(tile.lineLayers);
|
||||
PolygonLayers.clear(tile.polygonLayers);
|
||||
tile.lineLayers = null;
|
||||
tile.polygonLayers = null;
|
||||
tile.newData = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log.d(TAG, "uploadTileData, " + tile);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
|
||||
|
||||
sbuf = shortBuffer[uploadCnt];
|
||||
|
||||
// add fill coordinates
|
||||
newSize += 8;
|
||||
|
||||
// FIXME probably not a good idea to do this in gl thread...
|
||||
if (sbuf == null || sbuf.capacity() < newSize) {
|
||||
ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES).order(
|
||||
ByteOrder.nativeOrder());
|
||||
sbuf = bbuf.asShortBuffer();
|
||||
shortBuffer[uploadCnt] = sbuf;
|
||||
sbuf.put(mFillCoords, 0, 8);
|
||||
}
|
||||
|
||||
sbuf.clear();
|
||||
sbuf.position(8);
|
||||
|
||||
PolygonLayers.compileLayerData(tile.polygonLayers, sbuf);
|
||||
|
||||
tile.lineOffset = (8 + polySize);
|
||||
if (tile.lineOffset != sbuf.position())
|
||||
Log.d(TAG, "tiles lineoffset is wrong: " + tile + " "
|
||||
+ tile.lineOffset + " "
|
||||
+ sbuf.position() + " "
|
||||
+ sbuf.limit() + " "
|
||||
+ sbuf.remaining() + " "
|
||||
+ PolygonLayers.sizeOf(tile.polygonLayers) + " "
|
||||
+ tile.rel);
|
||||
|
||||
tile.lineOffset *= SHORT_BYTES;
|
||||
|
||||
LineLayers.compileLayerData(tile.lineLayers, sbuf);
|
||||
|
||||
sbuf.flip();
|
||||
|
||||
if (newSize != sbuf.remaining())
|
||||
Log.d(TAG, "tiles wrong: " + tile + " "
|
||||
+ newSize + " "
|
||||
+ sbuf.position() + " "
|
||||
+ sbuf.limit() + " "
|
||||
+ sbuf.remaining() + " "
|
||||
+ LineLayers.sizeOf(tile.lineLayers)
|
||||
+ tile.isLoading + " "
|
||||
+ tile.rel);
|
||||
|
||||
newSize *= SHORT_BYTES;
|
||||
|
||||
if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4
|
||||
&& mBufferMemoryUsage < LIMIT_BUFFERS) {
|
||||
GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf);
|
||||
} else {
|
||||
mBufferMemoryUsage -= tile.vbo.size;
|
||||
tile.vbo.size = newSize;
|
||||
glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW);
|
||||
mBufferMemoryUsage += tile.vbo.size;
|
||||
}
|
||||
|
||||
uploadCnt++;
|
||||
|
||||
tile.isReady = true;
|
||||
tile.newData = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void checkBufferUsage() {
|
||||
// try to clear some unused vbo when exceding limit
|
||||
if (mBufferMemoryUsage > LIMIT_BUFFERS) {
|
||||
Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB");
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
int buf[] = new int[1];
|
||||
|
||||
synchronized (mVBOs) {
|
||||
for (VertexBufferObject vbo : mVBOs) {
|
||||
|
||||
if (vbo.size == 0)
|
||||
continue;
|
||||
|
||||
mBufferMemoryUsage -= vbo.size;
|
||||
|
||||
// this should free allocated memory but it does not.
|
||||
// on HTC it causes oom exception?!
|
||||
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
|
||||
// glBufferData(GL_ARRAY_BUFFER, 0, null, GLES20.GL_STATIC_DRAW);
|
||||
|
||||
// recreate vbo instead
|
||||
buf[0] = vbo.id;
|
||||
GLES20.glDeleteBuffers(1, buf, 0);
|
||||
GLES20.glGenBuffers(1, buf, 0);
|
||||
vbo.id = buf[0];
|
||||
|
||||
vbo.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
Log.d(TAG, " > " + mBufferMemoryUsage / MB + "MB");
|
||||
|
||||
if (mBufferMemoryUsage > LIMIT_BUFFERS && CACHE_TILES > 100)
|
||||
CACHE_TILES -= 50;
|
||||
|
||||
} else if (CACHE_TILES < CACHE_TILES_MAX) {
|
||||
CACHE_TILES += 50;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 glUnused) {
|
||||
long start = 0;
|
||||
MapPosition mapPosition;
|
||||
|
||||
if (MapView.debugFrameTime)
|
||||
start = SystemClock.uptimeMillis();
|
||||
|
||||
if (mRotate != (mMapView.enableRotation || mMapView.enableCompass)) {
|
||||
Matrix.setIdentityM(mMVPMatrix, 0);
|
||||
mRotate = mMapView.enableRotation || mMapView.enableCompass;
|
||||
}
|
||||
if (mUpdateColor && mClearColor != null) {
|
||||
glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]);
|
||||
mUpdateColor = false;
|
||||
}
|
||||
|
||||
GLES20.glDepthMask(true);
|
||||
|
||||
// Note: having the impression it is faster to also clear the
|
||||
// stencil buffer even when not needed. probaly otherwise it
|
||||
// is masked out from the depth buffer as they share the same
|
||||
// memory region afaik
|
||||
glClear(GLES20.GL_COLOR_BUFFER_BIT
|
||||
| GLES20.GL_DEPTH_BUFFER_BIT
|
||||
| GLES20.GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// get position and current tiles to draw
|
||||
synchronized (MapRenderer.lock) {
|
||||
mapPosition = mCurPosition;
|
||||
|
||||
if (mUpdateTiles) {
|
||||
TilesData tmp = drawTiles;
|
||||
drawTiles = curTiles;
|
||||
curTiles = tmp;
|
||||
mUpdateTiles = false;
|
||||
}
|
||||
}
|
||||
|
||||
int tileCnt = drawTiles.cnt;
|
||||
MapTile[] tiles = drawTiles.tiles;
|
||||
|
||||
uploadCnt = 0;
|
||||
int updateTextures = 0;
|
||||
|
||||
// check visible tiles, upload new vertex data
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
MapTile tile = tiles[i];
|
||||
|
||||
if (!isVisible(mapPosition, tile, 1))
|
||||
continue;
|
||||
|
||||
if (tile.texture == null && TextRenderer.drawToTexture(tile))
|
||||
updateTextures++;
|
||||
|
||||
if (tile.newData) {
|
||||
uploadTileData(tile);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tile.isReady) {
|
||||
// check near relatives if they can serve as proxy
|
||||
MapTile rel = tile.rel.parent.tile;
|
||||
if (rel != null && rel.newData) {
|
||||
uploadTileData(rel);
|
||||
} else {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
if (tile.rel.child[c] == null)
|
||||
continue;
|
||||
|
||||
rel = tile.rel.child[c].tile;
|
||||
if (rel != null && rel.newData)
|
||||
uploadTileData(rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadCnt > 0)
|
||||
checkBufferUsage();
|
||||
|
||||
if (updateTextures > 0)
|
||||
TextRenderer.compileTextures();
|
||||
|
||||
if (mRotate) {
|
||||
Matrix.setRotateM(mRotateMatrix, 0, mapPosition.angle, 0, 0, 1);
|
||||
Matrix.transposeM(mRotTMatrix, 0, mRotateMatrix, 0);
|
||||
} else {
|
||||
Matrix.setIdentityM(mRotTMatrix, 0);
|
||||
}
|
||||
|
||||
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
|
||||
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
if (tiles[i].isVisible && tiles[i].isReady) {
|
||||
drawTile(mapPosition, tiles[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
// proxies are clipped to the region where nothing was drawn to depth buffer
|
||||
// TODO draw all parent before grandparent
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
if (tiles[i].isVisible && !tiles[i].isReady) {
|
||||
drawProxyTile(mapPosition, tiles[i]);
|
||||
}
|
||||
}
|
||||
// GlUtils.checkGlError("end draw");
|
||||
|
||||
glDisable(GLES20.GL_DEPTH_TEST);
|
||||
|
||||
mDrawCount = 0;
|
||||
mDrawSerial++;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
int z = mapPosition.zoomLevel;
|
||||
float s = mapPosition.scale;
|
||||
|
||||
int zoomLevelDiff = Math.max(z - MapGenerator.STROKE_MAX_ZOOM_LEVEL, 0);
|
||||
float scale = (float) Math.pow(1.4, zoomLevelDiff);
|
||||
if (scale < 1)
|
||||
scale = 1;
|
||||
|
||||
if (z >= MapGenerator.STROKE_MAX_ZOOM_LEVEL)
|
||||
TextRenderer.beginDraw(FloatMath.sqrt(s) / scale, mRotTMatrix);
|
||||
else
|
||||
TextRenderer.beginDraw(s, mRotTMatrix);
|
||||
|
||||
for (int i = 0; i < tileCnt; i++) {
|
||||
if (!tiles[i].isVisible || tiles[i].texture == null)
|
||||
continue;
|
||||
|
||||
setMatrix(mapPosition, tiles[i], 1, 0);
|
||||
TextRenderer.drawTile(tiles[i], mMVPMatrix);
|
||||
}
|
||||
TextRenderer.endDraw();
|
||||
|
||||
if (MapView.debugFrameTime) {
|
||||
GLES20.glFinish();
|
||||
Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start));
|
||||
}
|
||||
}
|
||||
|
||||
// used to not draw a tile twice per frame...
|
||||
private static byte mDrawSerial = 0;
|
||||
|
||||
private static void drawTile(MapPosition mapPosition, MapTile tile, float div) {
|
||||
// draw parents only once
|
||||
if (tile.lastDraw == mDrawSerial)
|
||||
return;
|
||||
|
||||
tile.lastDraw = mDrawSerial;
|
||||
|
||||
int z = mapPosition.zoomLevel;
|
||||
float s = mapPosition.scale;
|
||||
|
||||
// mDrawCount is used to calculation z offset.
|
||||
// (used for depth clipping)
|
||||
setMatrix(mapPosition, tile, div, mDrawCount++);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
|
||||
|
||||
LineLayer ll = tile.lineLayers;
|
||||
PolygonLayer pl = tile.polygonLayers;
|
||||
|
||||
boolean clipped = false;
|
||||
|
||||
for (; pl != null || ll != null;) {
|
||||
int lnext = Integer.MAX_VALUE;
|
||||
int pnext = Integer.MAX_VALUE;
|
||||
|
||||
if (ll != null)
|
||||
lnext = ll.layer;
|
||||
|
||||
if (pl != null)
|
||||
pnext = pl.layer;
|
||||
|
||||
if (pl != null && pnext < lnext) {
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
pl = PolygonLayers.drawPolygons(pl, lnext, mMVPMatrix, z, s, !clipped);
|
||||
|
||||
clipped = true;
|
||||
|
||||
} else {
|
||||
// XXX nasty
|
||||
if (!clipped) {
|
||||
PolygonLayers.drawPolygons(null, 0, mMVPMatrix, z, s, true);
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
ll = LineLayers.drawLines(tile, ll, pnext, mMVPMatrix, div, z, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean drawProxyChild(MapPosition mapPosition, MapTile tile) {
|
||||
int drawn = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (tile.rel.child[i] == null)
|
||||
continue;
|
||||
|
||||
MapTile c = tile.rel.child[i].tile;
|
||||
if (c == null)
|
||||
continue;
|
||||
|
||||
if (!isVisible(mapPosition, c, 2)) {
|
||||
drawn++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c.isReady) {
|
||||
drawTile(mapPosition, c, 2);
|
||||
drawn++;
|
||||
}
|
||||
}
|
||||
return drawn == 4;
|
||||
}
|
||||
|
||||
private static void drawProxyTile(MapPosition mapPosition, MapTile tile) {
|
||||
|
||||
if (mapPosition.scale > 1.5f) {
|
||||
// prefer drawing children
|
||||
if (!drawProxyChild(mapPosition, tile)) {
|
||||
MapTile t = tile.rel.parent.tile;
|
||||
if (t != null) {
|
||||
if (t.isReady) {
|
||||
drawTile(mapPosition, t, 0.5f);
|
||||
} else {
|
||||
MapTile p = t.rel.parent.tile;
|
||||
if (p != null && p.isReady)
|
||||
drawTile(mapPosition, p, 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// prefer drawing parent
|
||||
MapTile t = tile.rel.parent.tile;
|
||||
|
||||
if (t != null && t.isReady) {
|
||||
drawTile(mapPosition, t, 0.5f);
|
||||
|
||||
} else if (!drawProxyChild(mapPosition, tile)) {
|
||||
|
||||
// need to check rel.parent here, t could alread be root
|
||||
if (t != null) {
|
||||
t = t.rel.parent.tile;
|
||||
|
||||
if (t != null && t.isReady)
|
||||
drawTile(mapPosition, t, 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
|
||||
Log.d(TAG, "SurfaceChanged:" + width + " " + height);
|
||||
|
||||
drawTiles = curTiles = null;
|
||||
mBufferMemoryUsage = 0;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mAspect = (float) height / width;
|
||||
|
||||
Matrix.orthoM(mProjMatrix, 0, -0.5f / mAspect, 0.5f / mAspect, -0.5f, 0.5f, -1, 1);
|
||||
|
||||
// Matrix.frustumM(mProjMatrix, 0, -0.5f / mAspect, 0.5f / mAspect, -0.5f, 0.7f,
|
||||
// 1, 100);
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2)
|
||||
* (mHeight / (Tile.TILE_SIZE / 2) + 2);
|
||||
|
||||
TileLoader.init(mMapView, width, height, numTiles);
|
||||
|
||||
drawTiles = new TilesData(numTiles);
|
||||
curTiles = new TilesData(numTiles);
|
||||
|
||||
// Set up vertex buffer objects
|
||||
int numVBO = (CACHE_TILES + (numTiles * 2));
|
||||
int[] mVboIds = new int[numVBO];
|
||||
glGenBuffers(numVBO, mVboIds, 0);
|
||||
GlUtils.checkGlError("glGenBuffers");
|
||||
|
||||
mVBOs = new ArrayList<VertexBufferObject>(numVBO);
|
||||
|
||||
for (int i = 1; i < numVBO; i++)
|
||||
mVBOs.add(new VertexBufferObject(mVboIds[i]));
|
||||
|
||||
// Set up textures
|
||||
TextRenderer.init(numTiles);
|
||||
|
||||
if (mClearColor != null) {
|
||||
glClearColor(mClearColor[0], mClearColor[1],
|
||||
mClearColor[2], mClearColor[3]);
|
||||
} else {
|
||||
glClearColor(0.98f, 0.98f, 0.97f, 1.0f);
|
||||
}
|
||||
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// glEnable(GL_SCISSOR_TEST);
|
||||
// glScissor(0, 0, mWidth, mHeight);
|
||||
|
||||
glDisable(GLES20.GL_CULL_FACE);
|
||||
|
||||
glBlendFunc(GLES20.GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GlUtils.checkGlError("onSurfaceChanged");
|
||||
|
||||
mMapView.redrawTiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
|
||||
// String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
|
||||
// Log.d(TAG, "Extensions: " + ext);
|
||||
|
||||
shortBuffer = new ShortBuffer[rotateBuffers];
|
||||
|
||||
// add half pixel to tile clip/fill coordinates
|
||||
// to avoid rounding issues
|
||||
short min = -4;
|
||||
short max = (short) ((Tile.TILE_SIZE << 3) + 4);
|
||||
mFillCoords = new short[8];
|
||||
mFillCoords[0] = min;
|
||||
mFillCoords[1] = max;
|
||||
mFillCoords[2] = max;
|
||||
mFillCoords[3] = max;
|
||||
mFillCoords[4] = min;
|
||||
mFillCoords[5] = min;
|
||||
mFillCoords[6] = max;
|
||||
mFillCoords[7] = min;
|
||||
|
||||
LineLayers.init();
|
||||
PolygonLayers.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMapGenerator createMapGenerator() {
|
||||
return new MapGenerator(mMapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderTheme(RenderTheme t) {
|
||||
int bg = t.getMapBackground();
|
||||
float[] c = new float[4];
|
||||
c[0] = (bg >> 16 & 0xff) / 255.0f;
|
||||
c[1] = (bg >> 8 & 0xff) / 255.0f;
|
||||
c[2] = (bg >> 0 & 0xff) / 255.0f;
|
||||
c[3] = (bg >> 24 & 0xff) / 255.0f;
|
||||
mClearColor = c;
|
||||
mUpdateColor = true;
|
||||
}
|
||||
|
||||
}
|
||||
73
src/org/oscim/view/glrenderer/MapTile.java
Normal file
73
src/org/oscim/view/glrenderer/MapTile.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
|
||||
class MapTile extends JobTile {
|
||||
byte lastDraw = 0;
|
||||
|
||||
/**
|
||||
* VBO layout:
|
||||
* - 16 bytes fill coordinates
|
||||
* - n bytes polygon vertices
|
||||
* - m bytes lines vertices
|
||||
*/
|
||||
VertexBufferObject vbo;
|
||||
|
||||
/**
|
||||
* polygonOffset in vbo is always 16 bytes,
|
||||
*/
|
||||
int lineOffset;
|
||||
|
||||
TextTexture texture;
|
||||
|
||||
/**
|
||||
* Tile data set by MapGenerator:
|
||||
*/
|
||||
LineLayer lineLayers;
|
||||
PolygonLayer polygonLayers;
|
||||
TextItem labels;
|
||||
|
||||
/**
|
||||
* tile has new data to upload to gl
|
||||
*/
|
||||
boolean newData;
|
||||
|
||||
/**
|
||||
* tile is loaded and ready for drawing.
|
||||
*/
|
||||
boolean isReady;
|
||||
|
||||
/**
|
||||
* tile is in view region.
|
||||
*/
|
||||
boolean isVisible;
|
||||
|
||||
/**
|
||||
* tile is used by render thread. set by updateVisibleList (main thread).
|
||||
*/
|
||||
boolean isActive;
|
||||
|
||||
/**
|
||||
* pointer to access relatives in TileTree
|
||||
*/
|
||||
QuadTree rel;
|
||||
|
||||
MapTile(int tileX, int tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
}
|
||||
|
||||
}
|
||||
97
src/org/oscim/view/glrenderer/PolygonLayer.java
Normal file
97
src/org/oscim/view/glrenderer/PolygonLayer.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.renderinstruction.Area;
|
||||
|
||||
class PolygonLayer {
|
||||
private static final float S = MapRenderer.COORD_MULTIPLIER;
|
||||
|
||||
PolygonLayer next;
|
||||
Area area;
|
||||
|
||||
ShortItem pool;
|
||||
protected ShortItem curItem;
|
||||
int verticesCnt;
|
||||
int offset;
|
||||
|
||||
final int layer;
|
||||
|
||||
PolygonLayer(int layer, Area area) {
|
||||
this.layer = layer;
|
||||
this.area = area;
|
||||
curItem = ShortPool.get();
|
||||
pool = curItem;
|
||||
}
|
||||
|
||||
void addPolygon(float[] points, short[] index) {
|
||||
short center = (short) ((Tile.TILE_SIZE >> 1) * S);
|
||||
|
||||
ShortItem si = curItem;
|
||||
short[] v = si.vertices;
|
||||
int outPos = si.used;
|
||||
|
||||
for (int i = 0, pos = 0, n = index.length; i < n; i++) {
|
||||
int length = index[i];
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
// need at least three points
|
||||
if (length < 6) {
|
||||
pos += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
verticesCnt += length / 2 + 2;
|
||||
|
||||
int inPos = pos;
|
||||
|
||||
if (outPos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
outPos = 0;
|
||||
}
|
||||
|
||||
v[outPos++] = center;
|
||||
v[outPos++] = center;
|
||||
|
||||
for (int j = 0; j < length; j += 2) {
|
||||
if (outPos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
outPos = 0;
|
||||
}
|
||||
v[outPos++] = (short) (points[inPos++] * S);
|
||||
v[outPos++] = (short) (points[inPos++] * S);
|
||||
}
|
||||
|
||||
if (outPos == ShortItem.SIZE) {
|
||||
si = si.next = ShortPool.get();
|
||||
v = si.vertices;
|
||||
outPos = 0;
|
||||
}
|
||||
|
||||
v[outPos++] = (short) (points[pos + 0] * S);
|
||||
v[outPos++] = (short) (points[pos + 1] * S);
|
||||
|
||||
pos += length;
|
||||
}
|
||||
|
||||
si.used = outPos;
|
||||
curItem = si;
|
||||
}
|
||||
|
||||
}
|
||||
294
src/org/oscim/view/glrenderer/PolygonLayers.java
Normal file
294
src/org/oscim/view/glrenderer/PolygonLayers.java
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import static android.opengl.GLES20.GL_BLEND;
|
||||
import static android.opengl.GLES20.GL_EQUAL;
|
||||
import static android.opengl.GLES20.GL_INVERT;
|
||||
import static android.opengl.GLES20.GL_STENCIL_TEST;
|
||||
import static android.opengl.GLES20.GL_TRIANGLE_FAN;
|
||||
import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
|
||||
import static android.opengl.GLES20.GL_ZERO;
|
||||
import static android.opengl.GLES20.glColorMask;
|
||||
import static android.opengl.GLES20.glDisable;
|
||||
import static android.opengl.GLES20.glDrawArrays;
|
||||
import static android.opengl.GLES20.glEnable;
|
||||
import static android.opengl.GLES20.glGetAttribLocation;
|
||||
import static android.opengl.GLES20.glGetUniformLocation;
|
||||
import static android.opengl.GLES20.glStencilFunc;
|
||||
import static android.opengl.GLES20.glStencilMask;
|
||||
import static android.opengl.GLES20.glStencilOp;
|
||||
import static android.opengl.GLES20.glUniform4f;
|
||||
import static android.opengl.GLES20.glUniform4fv;
|
||||
import static android.opengl.GLES20.glUniformMatrix4fv;
|
||||
import static android.opengl.GLES20.glUseProgram;
|
||||
import static android.opengl.GLES20.glVertexAttribPointer;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.view.utils.GlUtils;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
|
||||
class PolygonLayers {
|
||||
private static final int NUM_VERTEX_SHORTS = 2;
|
||||
private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
|
||||
private static int STENCIL_BITS = 8;
|
||||
|
||||
private static PolygonLayer[] mFillPolys;
|
||||
|
||||
private static int polygonProgram;
|
||||
private static int hPolygonVertexPosition;
|
||||
private static int hPolygonMatrix;
|
||||
private static int hPolygonColor;
|
||||
|
||||
static boolean init() {
|
||||
|
||||
// Set up the program for rendering polygons
|
||||
polygonProgram = GlUtils.createProgram(Shaders.polygonVertexShader,
|
||||
Shaders.polygonFragmentShader);
|
||||
if (polygonProgram == 0) {
|
||||
// Log.e(TAG, "Could not create polygon program.");
|
||||
return false;
|
||||
}
|
||||
hPolygonMatrix = glGetUniformLocation(polygonProgram, "mvp");
|
||||
hPolygonVertexPosition = glGetAttribLocation(polygonProgram, "a_position");
|
||||
hPolygonColor = glGetUniformLocation(polygonProgram, "u_color");
|
||||
|
||||
mFillPolys = new PolygonLayer[STENCIL_BITS];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void fillPolygons(int count, double zoom, float scale) {
|
||||
boolean blend = false;
|
||||
|
||||
// draw to framebuffer
|
||||
glColorMask(true, true, true, true);
|
||||
|
||||
// do not modify stencil buffer
|
||||
glStencilMask(0);
|
||||
|
||||
glEnable(GLES20.GL_DEPTH_TEST);
|
||||
|
||||
for (int c = 0; c < count; c++) {
|
||||
PolygonLayer l = mFillPolys[c];
|
||||
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (l.area.fade >= zoom || l.area.color[3] != 1.0) {
|
||||
// draw alpha blending, fade in/out
|
||||
if (l.area.fade >= zoom) {
|
||||
alpha = (scale > 1.3f ? scale : 1.3f) - alpha;
|
||||
if (alpha > 1.0f)
|
||||
alpha = 1.0f;
|
||||
}
|
||||
|
||||
alpha *= l.area.color[3];
|
||||
|
||||
if (!blend) {
|
||||
glEnable(GL_BLEND);
|
||||
blend = true;
|
||||
}
|
||||
glUniform4f(hPolygonColor,
|
||||
l.area.color[0] * alpha,
|
||||
l.area.color[1] * alpha,
|
||||
l.area.color[2] * alpha,
|
||||
alpha);
|
||||
|
||||
} else if (l.area.blend == zoom) {
|
||||
// fade in/out
|
||||
alpha = scale - 1.0f;
|
||||
if (alpha > 1.0f)
|
||||
alpha = 1.0f;
|
||||
else if (alpha < 0)
|
||||
alpha = 0;
|
||||
|
||||
glUniform4f(hPolygonColor,
|
||||
l.area.color[0] * (1 - alpha) + l.area.blendColor[0] * alpha,
|
||||
l.area.color[1] * (1 - alpha) + l.area.blendColor[1] * alpha,
|
||||
l.area.color[2] * (1 - alpha) + l.area.blendColor[2] * alpha, 1);
|
||||
} else {
|
||||
// draw solid
|
||||
if (blend) {
|
||||
glDisable(GL_BLEND);
|
||||
blend = false;
|
||||
}
|
||||
if (l.area.blend <= zoom && l.area.blend > 0)
|
||||
glUniform4fv(hPolygonColor, 1, l.area.blendColor, 0);
|
||||
else
|
||||
glUniform4fv(hPolygonColor, 1, l.area.color, 0);
|
||||
}
|
||||
|
||||
// set stencil buffer mask used to draw this layer
|
||||
glStencilFunc(GL_EQUAL, 0xff, 1 << c);
|
||||
|
||||
// draw tile fill coordinates
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
if (blend)
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
static PolygonLayer drawPolygons(PolygonLayer layer, int next,
|
||||
float[] matrix, double zoom, float scale, boolean clip) {
|
||||
int cnt = 0;
|
||||
|
||||
glUseProgram(polygonProgram);
|
||||
GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
|
||||
|
||||
glVertexAttribPointer(hPolygonVertexPosition, 2,
|
||||
GLES20.GL_SHORT, false, 0,
|
||||
POLYGON_VERTICES_DATA_POS_OFFSET);
|
||||
|
||||
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
|
||||
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
PolygonLayer l = layer;
|
||||
|
||||
boolean first = clip;
|
||||
|
||||
for (; l != null && l.layer < next; l = l.next) {
|
||||
// fade out polygon layers (set in RederTheme)
|
||||
if (l.area.fade > 0 && l.area.fade > zoom)
|
||||
continue;
|
||||
|
||||
if (cnt == 0) {
|
||||
// disable drawing to framebuffer
|
||||
glColorMask(false, false, false, false);
|
||||
|
||||
// never pass the test, i.e. always apply first stencil op (sfail)
|
||||
glStencilFunc(GLES20.GL_ALWAYS, 0, 0xff);
|
||||
|
||||
// clear stencilbuffer
|
||||
glStencilMask(0xFF);
|
||||
// glClear(GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// clear stencilbuffer (tile region)
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
|
||||
|
||||
if (first) {
|
||||
// draw clip-region into depth buffer
|
||||
GLES20.glDepthMask(true);
|
||||
// to prevent overdraw gl_less restricts
|
||||
// the clip to the area where no other
|
||||
// tile was drawn
|
||||
GLES20.glDepthFunc(GLES20.GL_LESS);
|
||||
}
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
GLES20.glDepthMask(false);
|
||||
GLES20.glDepthFunc(GLES20.GL_EQUAL);
|
||||
}
|
||||
|
||||
// stencil op for stencil method polygon drawing
|
||||
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
|
||||
glDisable(GLES20.GL_DEPTH_TEST);
|
||||
}
|
||||
mFillPolys[cnt] = l;
|
||||
|
||||
// set stencil mask to draw to
|
||||
glStencilMask(1 << cnt++);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt);
|
||||
|
||||
// draw up to 8 layers into stencil buffer
|
||||
if (cnt == STENCIL_BITS) {
|
||||
fillPolygons(cnt, zoom, scale);
|
||||
cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 0)
|
||||
fillPolygons(cnt, zoom, scale);
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
if (clip && first)
|
||||
drawDepthClip();
|
||||
|
||||
// required on GalaxyII, Android 2.3.3 (cant just VAA enable once...)
|
||||
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
|
||||
return l;
|
||||
}
|
||||
|
||||
static void drawDepthClip() {
|
||||
glColorMask(false, false, false, false);
|
||||
GLES20.glDepthMask(true);
|
||||
GLES20.glDepthFunc(GLES20.GL_LESS);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
GLES20.glDepthMask(false);
|
||||
glColorMask(true, true, true, true);
|
||||
GLES20.glDepthFunc(GLES20.GL_EQUAL);
|
||||
}
|
||||
|
||||
static int sizeOf(PolygonLayer layers) {
|
||||
int size = 0;
|
||||
|
||||
for (PolygonLayer l = layers; l != null; l = l.next)
|
||||
size += l.verticesCnt;
|
||||
|
||||
size *= NUM_VERTEX_SHORTS;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void compileLayerData(PolygonLayer layers, ShortBuffer sbuf) {
|
||||
int pos = 4;
|
||||
|
||||
ShortItem last = null, items = null;
|
||||
|
||||
for (PolygonLayer l = layers; l != null; l = l.next) {
|
||||
|
||||
for (ShortItem item = l.pool; item != null; item = item.next) {
|
||||
|
||||
if (item.next == null) {
|
||||
sbuf.put(item.vertices, 0, item.used);
|
||||
} else {
|
||||
// item.used = ShortItem.SIZE;
|
||||
sbuf.put(item.vertices);
|
||||
}
|
||||
|
||||
last = item;
|
||||
}
|
||||
|
||||
l.offset = pos;
|
||||
pos += l.verticesCnt;
|
||||
|
||||
if (last != null) {
|
||||
last.next = items;
|
||||
items = l.pool;
|
||||
}
|
||||
|
||||
l.pool = null;
|
||||
}
|
||||
|
||||
ShortPool.add(items);
|
||||
}
|
||||
|
||||
static void clear(PolygonLayer layers) {
|
||||
for (PolygonLayer l = layers; l != null; l = l.next) {
|
||||
if (l.pool != null)
|
||||
ShortPool.add(l.pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
src/org/oscim/view/glrenderer/QuadTree.java
Normal file
157
src/org/oscim/view/glrenderer/QuadTree.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
class QuadTree {
|
||||
private static String TAG = "QuadTree";
|
||||
|
||||
// pointer to tile 0/0/0
|
||||
private static QuadTree root;
|
||||
|
||||
// parent pointer is used to link pool items
|
||||
private static QuadTree pool;
|
||||
|
||||
QuadTree parent;
|
||||
// .... x y
|
||||
// 0 => 0 0
|
||||
// 1 => 1 0
|
||||
// 2 => 0 1
|
||||
// 3 => 1 1
|
||||
final QuadTree[] child = new QuadTree[4];
|
||||
int refs = 0;
|
||||
byte id;
|
||||
MapTile tile;
|
||||
|
||||
static void init() {
|
||||
|
||||
pool = null;
|
||||
root = new QuadTree();
|
||||
root.parent = root;
|
||||
|
||||
QuadTree t;
|
||||
for (int i = 0; i < 200; i++) {
|
||||
t = new QuadTree();
|
||||
t.parent = pool;
|
||||
pool = t;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean remove(MapTile t) {
|
||||
if (t.rel == null) {
|
||||
Log.d(TAG, "already removed " + t);
|
||||
return true;
|
||||
}
|
||||
|
||||
QuadTree cur = t.rel;
|
||||
QuadTree next;
|
||||
|
||||
for (; cur != root;) {
|
||||
// keep pointer to parent
|
||||
next = cur.parent;
|
||||
cur.refs--;
|
||||
|
||||
// if current node has no children
|
||||
if (cur.refs == 0) {
|
||||
// unhook from parent
|
||||
next.child[cur.id] = null;
|
||||
|
||||
// add item back to pool
|
||||
cur.parent = pool;
|
||||
pool = cur;
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
|
||||
root.refs--;
|
||||
|
||||
t.rel.tile = null;
|
||||
t.rel = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static QuadTree add(MapTile tile) {
|
||||
|
||||
int x = tile.tileX;
|
||||
int y = tile.tileY;
|
||||
int z = tile.zoomLevel;
|
||||
|
||||
QuadTree cur;
|
||||
|
||||
// if (x < 0 || x >= 1 << z) {
|
||||
// Log.d(TAG, "invalid position");
|
||||
// return null;
|
||||
// }
|
||||
// if (y < 0 || y >= 1 << z) {
|
||||
// Log.d(TAG, "invalid position");
|
||||
// return null;
|
||||
// }
|
||||
|
||||
QuadTree leaf = root;
|
||||
|
||||
for (int level = z - 1; level >= 0; level--) {
|
||||
|
||||
int id = ((x >> level) & 1) | ((y >> level) & 1) << 1;
|
||||
|
||||
leaf.refs++;
|
||||
|
||||
cur = leaf.child[id];
|
||||
|
||||
if (cur != null) {
|
||||
leaf = cur;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pool != null) {
|
||||
cur = pool;
|
||||
pool = pool.parent;
|
||||
} else {
|
||||
cur = new QuadTree();
|
||||
}
|
||||
|
||||
cur.refs = 0;
|
||||
cur.id = (byte) id;
|
||||
cur.parent = leaf;
|
||||
cur.parent.child[id] = cur;
|
||||
|
||||
leaf = cur;
|
||||
}
|
||||
|
||||
leaf.refs++;
|
||||
leaf.tile = tile;
|
||||
tile.rel = leaf;
|
||||
|
||||
return leaf;
|
||||
}
|
||||
|
||||
static MapTile getTile(int x, int y, int z) {
|
||||
QuadTree leaf = root;
|
||||
|
||||
for (int level = z - 1; level >= 0; level--) {
|
||||
|
||||
leaf = leaf.child[((x >> level) & 1) | ((y >> level) & 1) << 1];
|
||||
|
||||
if (leaf == null)
|
||||
return null;
|
||||
|
||||
if (level == 0) {
|
||||
return leaf.tile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
197
src/org/oscim/view/glrenderer/Shaders.java
Normal file
197
src/org/oscim/view/glrenderer/Shaders.java
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
class Shaders {
|
||||
|
||||
final static String lineVertexShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform mat4 mvp;"
|
||||
+ "attribute vec2 a_position;"
|
||||
+ "attribute vec2 a_st;"
|
||||
+ "varying vec2 v_st;"
|
||||
+ "uniform float u_width;"
|
||||
+ "const float dscale = 8.0/2048.0;"
|
||||
+ "void main() {"
|
||||
// scale extrusion to u_width pixel
|
||||
// just ignore the two most insignificant bits of a_st :)
|
||||
+ " vec2 dir = dscale * u_width * a_st;"
|
||||
+ " gl_Position = mvp * vec4(a_position + dir, 0.0,1.0);"
|
||||
// last two bits of a_st hold the texture coordinates
|
||||
+ " v_st = u_width * (abs(mod(a_st,4.0)) - 1.0);"
|
||||
// TODO use bit operations when available (gles 1.3)
|
||||
// + " v_st = u_width * vec2(ivec2(a_st) & 3 - 1);"
|
||||
+ "}";
|
||||
|
||||
// final static String lineVertexShader = ""
|
||||
// + "precision mediump float;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "attribute vec4 a_position;"
|
||||
// + "attribute vec2 a_st;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "uniform float u_width;"
|
||||
// + "const float dscale = 8.0/1000.0;"
|
||||
// + "void main() {"
|
||||
// + " vec2 dir = dscale * u_width * a_position.zw;"
|
||||
// + " gl_Position = mvp * vec4(a_position.xy + dir, 0.0,1.0);"
|
||||
// + " v_st = u_width * a_st;"
|
||||
// + "}";
|
||||
|
||||
final static String lineFragmentShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform float u_wscale;"
|
||||
+ "uniform float u_width;"
|
||||
+ "uniform vec4 u_color;"
|
||||
+ "varying vec2 v_st;"
|
||||
+ "const float zero = 0.0;"
|
||||
+ "void main() {"
|
||||
+ " float len;"
|
||||
+ " if (v_st.t == zero)"
|
||||
+ " len = abs(v_st.s);"
|
||||
+ " else "
|
||||
+ " len = length(v_st);"
|
||||
// fade to alpha. u_wscale is the width in pixel which should be faded,
|
||||
// u_width - len the position of this fragment on the perpendicular to
|
||||
// this line segment
|
||||
+ " gl_FragColor = smoothstep(zero, u_wscale, u_width - len) * u_color;"
|
||||
+ "}";
|
||||
|
||||
// final static String lineFragmentShader = ""
|
||||
// + "#extension GL_OES_standard_derivatives : enable\n"
|
||||
// + "precision mediump float;\n"
|
||||
// + "uniform float u_wscale;"
|
||||
// + "uniform float u_width;"
|
||||
// + "uniform vec4 u_color;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "const float zero = 0.0;"
|
||||
// + "void main() {"
|
||||
// + " vec4 color = u_color;"
|
||||
// + " float width = u_width;"
|
||||
// + " float len;"
|
||||
// + " if (v_st.t == zero)"
|
||||
// + " len = abs(v_st.s);"
|
||||
// + " else "
|
||||
// + " len = length(v_st);"
|
||||
// + " vec2 st_width = fwidth(v_st);"
|
||||
// + " float fuzz = max(st_width.s, st_width.t) * 1.5;"
|
||||
// + " color.a *= smoothstep(zero, fuzz + u_wscale, u_width - len);"
|
||||
// + " gl_FragColor = color;"
|
||||
// + "}";
|
||||
|
||||
final static String polygonVertexShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform mat4 mvp;"
|
||||
+ "attribute vec4 a_position;"
|
||||
+ "void main() {"
|
||||
+ " gl_Position = mvp * a_position;"
|
||||
+ "}";
|
||||
|
||||
final static String polygonFragmentShader = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform vec4 u_color;"
|
||||
+ "void main() {"
|
||||
+ " gl_FragColor = u_color;"
|
||||
+ "}";
|
||||
|
||||
final static String textVertexShader = ""
|
||||
+ "precision highp float; "
|
||||
+ "attribute vec4 vertex;"
|
||||
+ "attribute vec2 tex_coord;"
|
||||
+ "uniform mat4 mvp;"
|
||||
+ "uniform mat4 rotation;"
|
||||
+ "uniform float scale;"
|
||||
+ "varying vec2 tex_c;"
|
||||
+ "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
|
||||
+ "void main() {"
|
||||
+ " if (mod(vertex.x, 2.0) == 0.0){"
|
||||
+ " gl_Position = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);"
|
||||
+ " } else {"
|
||||
+ " vec4 dir = rotation * vec4(vertex.zw / scale, 0.0, 1.0);"
|
||||
+ " gl_Position = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);"
|
||||
+ " }"
|
||||
+ " tex_c = tex_coord * div;"
|
||||
+ "}";
|
||||
|
||||
// final static String textVertexShader = ""
|
||||
// + "precision highp float; "
|
||||
// + "attribute vec4 vertex;"
|
||||
// + "attribute vec2 tex_coord;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "uniform mat4 rotation;"
|
||||
// + "uniform float scale;"
|
||||
// + "varying vec2 tex_c;"
|
||||
// + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
|
||||
// + "void main() {"
|
||||
// + " if (mod(vertex.x, 2.0) == 0.0){"
|
||||
// + " gl_Position = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);"
|
||||
// + " } else {"
|
||||
// + " vec4 dir = rotation * vec4(vertex.zw / scale, 0.0, 1.0);"
|
||||
// + " gl_Position = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);"
|
||||
// + " }"
|
||||
// + " tex_c = tex_coord * div;"
|
||||
// + "}";
|
||||
|
||||
final static String textFragmentShader = ""
|
||||
+ "precision highp float;"
|
||||
+ "uniform sampler2D tex;"
|
||||
+ "uniform vec4 col;"
|
||||
+ "varying vec2 tex_c;"
|
||||
+ "void main() {"
|
||||
+ " gl_FragColor = texture2D(tex, tex_c.xy);"
|
||||
+ "}";
|
||||
|
||||
// final static String lineVertexZigZagShader = ""
|
||||
// + "precision mediump float;"
|
||||
// + "uniform mat4 mvp;"
|
||||
// + "attribute vec4 a_pos1;"
|
||||
// + "attribute vec2 a_st1;"
|
||||
// + "attribute vec4 a_pos2;"
|
||||
// + "attribute vec2 a_st2;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "uniform vec2 u_mode;"
|
||||
// + "const float dscale = 1.0/1000.0;"
|
||||
// + "void main() {"
|
||||
// + "if (gl_VertexID & 1 == 0) {"
|
||||
// + " vec2 dir = dscale * u_mode[1] * a_pos1.zw;"
|
||||
// + " gl_Position = mvp * vec4(a_pos1.xy + dir, 0.0,1.0);"
|
||||
// + " v_st = u_mode[1] * a_st1;"
|
||||
// + "} else {"
|
||||
// + " vec2 dir = dscale * u_mode[1] * a_pos2.zw;"
|
||||
// + " gl_Position = mvp * vec4( a_pos1.xy, dir, 0.0,1.0);"
|
||||
// + " v_st = u_mode[1] * vec2(-a_st2.s , a_st2.t);"
|
||||
// + "}";
|
||||
|
||||
// final static String lineFragmentShader = ""
|
||||
// + "#extension GL_OES_standard_derivatives : enable\n"
|
||||
// + "precision mediump float;"
|
||||
// + "uniform vec2 u_mode;"
|
||||
// + "uniform vec4 u_color;"
|
||||
// + "varying vec2 v_st;"
|
||||
// + "const float zero = 0.0;"
|
||||
// + "void main() {"
|
||||
// + " vec4 color = u_color;"
|
||||
// + " float width = u_mode[1];"
|
||||
// + " float len;"
|
||||
// + " if (v_st.t == zero)"
|
||||
// + " len = abs(v_st.s);"
|
||||
// + " else "
|
||||
// + " len = length(v_st);"
|
||||
// + " vec2 st_width = fwidth(v_st);"
|
||||
// + " float fuzz = max(st_width.s, st_width.t);"
|
||||
// + " color.a *= smoothstep(-pixel, fuzz*pixel, width - (len * width));"
|
||||
// + " gl_FragColor = color;"
|
||||
// + "}";
|
||||
}
|
||||
29
src/org/oscim/view/glrenderer/ShortItem.java
Normal file
29
src/org/oscim/view/glrenderer/ShortItem.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
public class ShortItem {
|
||||
final short[] vertices;
|
||||
int used;
|
||||
ShortItem next;
|
||||
|
||||
ShortItem() {
|
||||
vertices = new short[SIZE];
|
||||
used = 0;
|
||||
}
|
||||
|
||||
// must be multiple of 4 (expected in LineLayer/PolygonLayer)
|
||||
static final int SIZE = 256;
|
||||
}
|
||||
111
src/org/oscim/view/glrenderer/ShortPool.java
Normal file
111
src/org/oscim/view/glrenderer/ShortPool.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class ShortPool {
|
||||
private static final int POOL_LIMIT = 6000;
|
||||
|
||||
static private ShortItem pool = null;
|
||||
static private int count = 0;
|
||||
static private int countAll = 0;
|
||||
|
||||
static synchronized void init() {
|
||||
count = 0;
|
||||
countAll = 0;
|
||||
pool = null;
|
||||
}
|
||||
|
||||
static synchronized ShortItem get() {
|
||||
|
||||
if (pool == null && count > 0) {
|
||||
Log.d("ShortPool", "XXX wrong count: " + count);
|
||||
}
|
||||
if (pool == null) {
|
||||
countAll++;
|
||||
return new ShortItem();
|
||||
}
|
||||
|
||||
count--;
|
||||
|
||||
if (count < 0) {
|
||||
int c = 0;
|
||||
|
||||
for (ShortItem tmp = pool; tmp != null; tmp = tmp.next)
|
||||
c++;
|
||||
|
||||
Log.d("ShortPool", "XXX wrong count: " + count + " left" + c);
|
||||
return new ShortItem();
|
||||
}
|
||||
|
||||
ShortItem it = pool;
|
||||
pool = pool.next;
|
||||
it.used = 0;
|
||||
it.next = null;
|
||||
return it;
|
||||
}
|
||||
|
||||
// private static float load = 1.0f;
|
||||
// private static int loadCount = 0;
|
||||
|
||||
static synchronized void add(ShortItem items) {
|
||||
if (items == null)
|
||||
return;
|
||||
|
||||
// int pall = countAll;
|
||||
// int pcnt = count;
|
||||
|
||||
// limit pool items
|
||||
if (countAll < POOL_LIMIT) {
|
||||
|
||||
ShortItem last = items;
|
||||
|
||||
while (true) {
|
||||
count++;
|
||||
// load += (float) last.used / ShortItem.SIZE;
|
||||
// loadCount++;
|
||||
|
||||
if (last.next == null)
|
||||
break;
|
||||
|
||||
last = last.next;
|
||||
}
|
||||
|
||||
last.next = pool;
|
||||
pool = items;
|
||||
// Log.d("Pool", "added: " + (count - pcnt) + " " + count + " " + countAll
|
||||
// + " load: " + (load / loadCount));
|
||||
|
||||
} else {
|
||||
// int cleared = 0;
|
||||
ShortItem prev, tmp = items;
|
||||
while (tmp != null) {
|
||||
prev = tmp;
|
||||
tmp = tmp.next;
|
||||
|
||||
countAll--;
|
||||
|
||||
// load += (float) prev.used / ShortItem.SIZE;
|
||||
// loadCount++;
|
||||
|
||||
prev.next = null;
|
||||
|
||||
}
|
||||
// Log.d("Pool", "dropped: " + (pall - countAll) + " " + count + " "
|
||||
// + countAll + " load: " + (load / loadCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/org/oscim/view/glrenderer/SymbolLayer.java
Normal file
19
src/org/oscim/view/glrenderer/SymbolLayer.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.oscim.view.glrenderer;
|
||||
|
||||
public class SymbolLayer {
|
||||
|
||||
}
|
||||
49
src/org/oscim/view/glrenderer/TextItem.java
Normal file
49
src/org/oscim/view/glrenderer/TextItem.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import org.oscim.theme.renderinstruction.Caption;
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
|
||||
public class TextItem {
|
||||
TextItem next;
|
||||
|
||||
final float x, y;
|
||||
final String text;
|
||||
final Caption caption;
|
||||
final PathText path;
|
||||
final float width;
|
||||
|
||||
short x1, y1, x2, y2;
|
||||
|
||||
public TextItem(float x, float y, String text, Caption caption) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.text = text;
|
||||
this.caption = caption;
|
||||
this.width = caption.paint.measureText(text);
|
||||
this.path = null;
|
||||
}
|
||||
|
||||
public TextItem(float x, float y, String text, PathText pathText, float width) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.text = text;
|
||||
this.path = pathText;
|
||||
this.caption = null;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
}
|
||||
476
src/org/oscim/view/glrenderer/TextRenderer.java
Normal file
476
src/org/oscim/view/glrenderer/TextRenderer.java
Normal file
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.oscim.view.utils.GlUtils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
public class TextRenderer {
|
||||
private final static int TEXTURE_WIDTH = 512;
|
||||
private final static int TEXTURE_HEIGHT = 256;
|
||||
private final static float SCALE = 8.0f;
|
||||
private final static int LBIT_MASK = 0xfffffffe;
|
||||
// private final static int L2BIT_MASK = 0xfffffffc;
|
||||
|
||||
final static int INDICES_PER_SPRITE = 6;
|
||||
final static int VERTICES_PER_SPRITE = 4;
|
||||
final static int SHORTS_PER_VERTICE = 6;
|
||||
final static int MAX_LABELS = 35;
|
||||
|
||||
private static Bitmap mBitmap;
|
||||
private static Canvas mCanvas;
|
||||
private static int mFontPadX = 1;
|
||||
private static int mFontPadY = 1;
|
||||
private static int mBitmapFormat;
|
||||
private static int mBitmapType;
|
||||
private static ShortBuffer mShortBuffer;
|
||||
private static TextTexture[] mTextures;
|
||||
|
||||
private static int mIndicesVBO;
|
||||
private static int mVerticesVBO;
|
||||
|
||||
private static int mTextProgram;
|
||||
private static int hTextUVPMatrix;
|
||||
private static int hTextRotationMatrix;
|
||||
private static int hTextVertex;
|
||||
private static int hTextScale;
|
||||
private static int hTextTextureCoord;
|
||||
|
||||
private static Paint mPaint = new Paint(Color.BLACK);
|
||||
|
||||
private static boolean debug = false;
|
||||
private static short[] debugVertices = {
|
||||
|
||||
0, 0,
|
||||
0, TEXTURE_HEIGHT * 4,
|
||||
|
||||
0, TEXTURE_HEIGHT - 1,
|
||||
0, 0,
|
||||
|
||||
TEXTURE_WIDTH - 1, 0,
|
||||
TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4,
|
||||
|
||||
TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1,
|
||||
TEXTURE_WIDTH * 4, 0,
|
||||
|
||||
};
|
||||
|
||||
static boolean init(int numTextures) {
|
||||
int bufferSize = numTextures
|
||||
* MAX_LABELS * VERTICES_PER_SPRITE
|
||||
* SHORTS_PER_VERTICE * (Short.SIZE / 8);
|
||||
|
||||
// if (mBitmap == null) {
|
||||
mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
mCanvas = new Canvas(mBitmap);
|
||||
|
||||
mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
|
||||
mBitmapType = GLUtils.getType(mBitmap);
|
||||
|
||||
ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
|
||||
mShortBuffer = buf.asShortBuffer();
|
||||
// }
|
||||
|
||||
mTextProgram = GlUtils.createProgram(Shaders.textVertexShader,
|
||||
Shaders.textFragmentShader);
|
||||
|
||||
hTextUVPMatrix = GLES20.glGetUniformLocation(mTextProgram, "mvp");
|
||||
hTextRotationMatrix = GLES20.glGetUniformLocation(mTextProgram, "rotation");
|
||||
|
||||
hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex");
|
||||
hTextScale = GLES20.glGetUniformLocation(mTextProgram, "scale");
|
||||
hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord");
|
||||
|
||||
int[] textureIds = new int[numTextures];
|
||||
TextTexture[] textures = new TextTexture[numTextures];
|
||||
GLES20.glGenTextures(numTextures, textureIds, 0);
|
||||
|
||||
for (int i = 0; i < numTextures; i++) {
|
||||
// setup filters for texture
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
|
||||
|
||||
// load the generated bitmap onto the texture
|
||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap,
|
||||
mBitmapType, 0);
|
||||
|
||||
textures[i] = new TextTexture(textureIds[i]);
|
||||
}
|
||||
|
||||
GlUtils.checkGlError("init textures");
|
||||
|
||||
mTextures = textures;
|
||||
|
||||
// Setup triangle indices
|
||||
short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE];
|
||||
int len = indices.length;
|
||||
short j = 0;
|
||||
for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) {
|
||||
indices[i + 0] = (short) (j + 0);
|
||||
indices[i + 1] = (short) (j + 1);
|
||||
indices[i + 2] = (short) (j + 2);
|
||||
indices[i + 3] = (short) (j + 2);
|
||||
indices[i + 4] = (short) (j + 3);
|
||||
indices[i + 5] = (short) (j + 0);
|
||||
// indices[i + 0] = (short) (j + 0);
|
||||
// indices[i + 1] = (short) (j + 0);
|
||||
// indices[i + 2] = (short) (j + 1);
|
||||
// indices[i + 3] = (short) (j + 3);
|
||||
// indices[i + 4] = (short) (j + 2);
|
||||
// indices[i + 5] = (short) (j + 2);
|
||||
}
|
||||
|
||||
mShortBuffer.clear();
|
||||
mShortBuffer.put(indices, 0, len);
|
||||
mShortBuffer.flip();
|
||||
|
||||
int[] mVboIds = new int[2];
|
||||
GLES20.glGenBuffers(2, mVboIds, 0);
|
||||
mIndicesVBO = mVboIds[0];
|
||||
mVerticesVBO = mVboIds[1];
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
|
||||
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
|
||||
mShortBuffer, GLES20.GL_STATIC_DRAW);
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
mShortBuffer.clear();
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
|
||||
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize,
|
||||
mShortBuffer, GLES20.GL_DYNAMIC_DRAW);
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean drawToTexture(MapTile tile) {
|
||||
TextTexture tex = null;
|
||||
|
||||
if (tile.labels == null)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < mTextures.length; i++) {
|
||||
tex = mTextures[i];
|
||||
if (tex.tile == null)
|
||||
break;
|
||||
if (!tex.tile.isActive)
|
||||
break;
|
||||
|
||||
tex = null;
|
||||
}
|
||||
|
||||
if (tex == null) {
|
||||
for (int i = 0; i < mTextures.length; i++) {
|
||||
tex = mTextures[i];
|
||||
if (!tex.tile.isVisible)
|
||||
break;
|
||||
|
||||
tex = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (tex == null) {
|
||||
Log.d(TAG, "no textures left");
|
||||
return false;
|
||||
}
|
||||
if (tex.tile != null)
|
||||
tex.tile.texture = null;
|
||||
|
||||
mBitmap.eraseColor(Color.TRANSPARENT);
|
||||
|
||||
int pos = 0;
|
||||
short[] buf = tex.vertices;
|
||||
|
||||
float y = 0;
|
||||
float x = mFontPadX;
|
||||
float width, height;
|
||||
|
||||
int max = MAX_LABELS;
|
||||
|
||||
if (debug) {
|
||||
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4],
|
||||
debugVertices[5], mPaint);
|
||||
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8],
|
||||
debugVertices[9], mPaint);
|
||||
|
||||
mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4],
|
||||
debugVertices[5], mPaint);
|
||||
mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8],
|
||||
debugVertices[9], mPaint);
|
||||
}
|
||||
|
||||
int advanceY = 0;
|
||||
|
||||
TextItem t = tile.labels;
|
||||
float yy;
|
||||
short x1, x2, x3, x4, y1, y2, y3, y4;
|
||||
|
||||
for (int i = 0; t != null && i < max; t = t.next, i++) {
|
||||
|
||||
if (t.caption != null) {
|
||||
height = (int) (t.caption.fontHeight) + 2 * mFontPadY;
|
||||
} else {
|
||||
height = (int) (t.path.fontHeight) + 2 * mFontPadY;
|
||||
}
|
||||
|
||||
width = t.width + 2 * mFontPadX;
|
||||
|
||||
if (height > advanceY)
|
||||
advanceY = (int) height;
|
||||
|
||||
if (x + width > TEXTURE_WIDTH) {
|
||||
x = mFontPadX;
|
||||
y += advanceY;
|
||||
advanceY = (int) height;
|
||||
}
|
||||
|
||||
if (t.caption != null) {
|
||||
yy = y + (height - 1) - t.caption.fontDescent - mFontPadY;
|
||||
} else {
|
||||
yy = y + (height - 1) - t.path.fontDescent - mFontPadY;
|
||||
}
|
||||
|
||||
if (yy > TEXTURE_HEIGHT) {
|
||||
Log.d(TAG, "reached max labels");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t.caption != null) {
|
||||
if (t.caption.stroke != null)
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.stroke);
|
||||
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.paint);
|
||||
} else {
|
||||
if (t.path.stroke != null)
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.stroke);
|
||||
|
||||
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.paint);
|
||||
}
|
||||
if (width > TEXTURE_WIDTH)
|
||||
width = TEXTURE_WIDTH;
|
||||
|
||||
float hw = width / 2.0f;
|
||||
float hh = height / 2.0f;
|
||||
|
||||
if (t.caption != null) {
|
||||
x1 = x3 = (short) (SCALE * (-hw));
|
||||
y1 = y3 = (short) (SCALE * (-hh));
|
||||
x2 = x4 = (short) (SCALE * (hw));
|
||||
y2 = y4 = (short) (SCALE * (hh));
|
||||
}
|
||||
else {
|
||||
float vx = t.x1 - t.x2;
|
||||
float vy = t.y1 - t.y2;
|
||||
float a = FloatMath.sqrt(vx * vx + vy * vy);
|
||||
vx = vx / a;
|
||||
vy = vy / a;
|
||||
|
||||
float ux = -vy;
|
||||
float uy = vx;
|
||||
|
||||
// int dx = (int) (vx * SCALE) & L2BIT_MASK;
|
||||
// int dy = (int) (vy * SCALE) & L2BIT_MASK;
|
||||
//
|
||||
// x1 = (short) dx;
|
||||
// y1 = (short) dy;
|
||||
//
|
||||
// x2 = (short) (dx | 1);
|
||||
// y3 = (short) (dy | 1);
|
||||
//
|
||||
// x4 = (short) (dx | 3);
|
||||
// y4 = (short) (dy | 3);
|
||||
//
|
||||
// x3 = (short) (dx | 2);
|
||||
// y2 = (short) (dy | 2);
|
||||
|
||||
x1 = (short) (SCALE * (vx * hw + ux * hh));
|
||||
y1 = (short) (SCALE * (vy * hw + uy * hh));
|
||||
x2 = (short) (SCALE * (-vx * hw + ux * hh));
|
||||
y3 = (short) (SCALE * (-vy * hw + uy * hh));
|
||||
x4 = (short) (SCALE * (-vx * hw - ux * hh));
|
||||
y4 = (short) (SCALE * (-vy * hw - uy * hh));
|
||||
x3 = (short) (SCALE * (vx * hw - ux * hh));
|
||||
y2 = (short) (SCALE * (vy * hw - uy * hh));
|
||||
|
||||
}
|
||||
short u1 = (short) (SCALE * x);
|
||||
short v1 = (short) (SCALE * y);
|
||||
short u2 = (short) (SCALE * (x + width));
|
||||
short v2 = (short) (SCALE * (y + height));
|
||||
|
||||
// pack caption/way-text info in lowest bit
|
||||
short tx;
|
||||
if (t.caption == null)
|
||||
tx = (short) ((int) (SCALE * t.x) & LBIT_MASK | 0);
|
||||
else
|
||||
tx = (short) ((int) (SCALE * t.x) & LBIT_MASK | 1);
|
||||
|
||||
short ty = (short) (SCALE * t.y);
|
||||
|
||||
// top-left
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x1;
|
||||
buf[pos++] = y1;
|
||||
buf[pos++] = u1;
|
||||
buf[pos++] = v2;
|
||||
|
||||
// top-right
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x2;
|
||||
buf[pos++] = y3;
|
||||
buf[pos++] = u2;
|
||||
buf[pos++] = v2;
|
||||
|
||||
// bot-right
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x4;
|
||||
buf[pos++] = y4;
|
||||
buf[pos++] = u2;
|
||||
buf[pos++] = v1;
|
||||
|
||||
// bot-left
|
||||
buf[pos++] = tx;
|
||||
buf[pos++] = ty;
|
||||
buf[pos++] = x3;
|
||||
buf[pos++] = y2;
|
||||
buf[pos++] = u1;
|
||||
buf[pos++] = v1;
|
||||
|
||||
x += width;
|
||||
|
||||
if (y > TEXTURE_HEIGHT) {
|
||||
Log.d(TAG, "reached max labels: texture is full");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tex.length = pos;
|
||||
tile.texture = tex;
|
||||
tex.tile = tile;
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id);
|
||||
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap,
|
||||
mBitmapFormat, mBitmapType);
|
||||
|
||||
// GLES20.glFlush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String TAG = "TextRenderer";
|
||||
|
||||
static void compileTextures() {
|
||||
int offset = 0;
|
||||
TextTexture tex;
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
|
||||
|
||||
mShortBuffer.clear();
|
||||
|
||||
for (int i = 0; i < mTextures.length; i++) {
|
||||
tex = mTextures[i];
|
||||
if (tex.tile == null || !tex.tile.isActive)
|
||||
continue;
|
||||
|
||||
mShortBuffer.put(tex.vertices, 0, tex.length);
|
||||
tex.offset = offset;
|
||||
offset += tex.length;
|
||||
}
|
||||
|
||||
mShortBuffer.flip();
|
||||
|
||||
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8),
|
||||
mShortBuffer);
|
||||
}
|
||||
|
||||
static void beginDraw(float scale, float[] rotation) {
|
||||
GLES20.glUseProgram(mTextProgram);
|
||||
|
||||
GLES20.glEnableVertexAttribArray(hTextTextureCoord);
|
||||
GLES20.glEnableVertexAttribArray(hTextVertex);
|
||||
|
||||
GLES20.glUniform1f(hTextScale, scale);
|
||||
GLES20.glUniformMatrix4fv(hTextRotationMatrix, 1, false, rotation, 0);
|
||||
|
||||
if (debug) {
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
|
||||
mShortBuffer.clear();
|
||||
mShortBuffer.put(debugVertices, 0, 16);
|
||||
mShortBuffer.flip();
|
||||
GLES20.glVertexAttribPointer(hTextVertex, 2,
|
||||
GLES20.GL_SHORT, false, 8, mShortBuffer);
|
||||
mShortBuffer.position(2);
|
||||
GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
|
||||
GLES20.GL_SHORT, false, 8, mShortBuffer);
|
||||
} else {
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
|
||||
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
|
||||
}
|
||||
}
|
||||
|
||||
static void endDraw() {
|
||||
|
||||
GLES20.glDisableVertexAttribArray(hTextTextureCoord);
|
||||
GLES20.glDisableVertexAttribArray(hTextVertex);
|
||||
}
|
||||
|
||||
static void drawTile(MapTile tile, float[] matrix) {
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id);
|
||||
|
||||
GLES20.glUniformMatrix4fv(hTextUVPMatrix, 1, false, matrix, 0);
|
||||
|
||||
if (debug) {
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
} else {
|
||||
|
||||
GLES20.glVertexAttribPointer(hTextVertex, 4,
|
||||
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8));
|
||||
|
||||
GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
|
||||
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)
|
||||
+ 8);
|
||||
|
||||
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) *
|
||||
INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/org/oscim/view/glrenderer/TextTexture.java
Normal file
34
src/org/oscim/view/glrenderer/TextTexture.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
public class TextTexture {
|
||||
|
||||
final short[] vertices;
|
||||
final int id;
|
||||
int length;
|
||||
int offset;
|
||||
MapTile tile;
|
||||
|
||||
String[] text;
|
||||
|
||||
TextTexture(int textureID) {
|
||||
vertices = new short[TextRenderer.MAX_LABELS *
|
||||
TextRenderer.VERTICES_PER_SPRITE *
|
||||
TextRenderer.SHORTS_PER_VERTICE];
|
||||
id = textureID;
|
||||
}
|
||||
|
||||
}
|
||||
514
src/org/oscim/view/glrenderer/TileLoader.java
Normal file
514
src/org/oscim/view/glrenderer/TileLoader.java
Normal file
@@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.glrenderer.MapRenderer.TilesData;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
// for lack of a better name... maybe TileManager?
|
||||
|
||||
/**
|
||||
* Update list of visible tiles and passes them to MapRenderer, when not available tiles are created and added to
|
||||
* JobQueue (mapView.addJobs) for loading by MapGenerator class
|
||||
*/
|
||||
|
||||
class TileLoader {
|
||||
private static final String TAG = "TileLoader";
|
||||
|
||||
private static final int MAX_TILES_IN_QUEUE = 40;
|
||||
|
||||
private static MapView mMapView;
|
||||
|
||||
// new jobs for the MapWorkers
|
||||
private static ArrayList<JobTile> mJobList;
|
||||
|
||||
// all tiles currently referenced
|
||||
private static ArrayList<MapTile> mTiles;
|
||||
// private static ArrayList<MapTile> mTilesActive;
|
||||
// tiles that have new data to upload, see passTile()
|
||||
private static ArrayList<MapTile> mTilesLoaded;
|
||||
|
||||
// current center tile, values used to check if position has
|
||||
// changed for updating current tile list
|
||||
private static long mTileX, mTileY;
|
||||
private static float mPrevScale;
|
||||
private static byte mPrevZoom;
|
||||
private static boolean mInitial;
|
||||
|
||||
// private static MapPosition mCurPosition, mDrawPosition;
|
||||
private static int mWidth, mHeight;
|
||||
|
||||
private static TilesData newTiles;
|
||||
|
||||
static void init(MapView mapView, int w, int h, int numTiles) {
|
||||
mMapView = mapView;
|
||||
|
||||
ShortPool.init();
|
||||
QuadTree.init();
|
||||
|
||||
mJobList = new ArrayList<JobTile>();
|
||||
mTiles = new ArrayList<MapTile>();
|
||||
mTilesLoaded = new ArrayList<MapTile>(30);
|
||||
|
||||
mInitial = true;
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
|
||||
newTiles = new TilesData(numTiles);
|
||||
}
|
||||
|
||||
static synchronized void updateMap(boolean clear) {
|
||||
|
||||
boolean changedPos = false;
|
||||
boolean changedZoom = false;
|
||||
|
||||
if (mMapView == null || mMapView.getMapPosition() == null)
|
||||
return;
|
||||
|
||||
MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
|
||||
if (mapPosition == null) {
|
||||
Log.d(TAG, ">>> no map position");
|
||||
return;
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
mInitial = true;
|
||||
synchronized (MapRenderer.lock) {
|
||||
|
||||
for (MapTile t : mTiles)
|
||||
clearTile(t);
|
||||
|
||||
mTiles.clear();
|
||||
mTilesLoaded.clear();
|
||||
QuadTree.init();
|
||||
}
|
||||
}
|
||||
|
||||
byte zoomLevel = mapPosition.zoomLevel;
|
||||
float scale = mapPosition.scale;
|
||||
|
||||
long tileX = MercatorProjection.pixelXToTileX(mapPosition.x, zoomLevel)
|
||||
* Tile.TILE_SIZE;
|
||||
long tileY = MercatorProjection.pixelYToTileY(mapPosition.y, zoomLevel)
|
||||
* Tile.TILE_SIZE;
|
||||
|
||||
int zdir = 0;
|
||||
if (mInitial || mPrevZoom != zoomLevel) {
|
||||
changedZoom = true;
|
||||
mPrevScale = scale;
|
||||
}
|
||||
else if (tileX != mTileX || tileY != mTileY) {
|
||||
if (mPrevScale - scale > 0 && scale > 1.2)
|
||||
zdir = 1;
|
||||
mPrevScale = scale;
|
||||
changedPos = true;
|
||||
}
|
||||
else if (mPrevScale - scale > 0.2 || mPrevScale - scale < -0.2) {
|
||||
if (mPrevScale - scale > 0 && scale > 1.2)
|
||||
zdir = 1;
|
||||
mPrevScale = scale;
|
||||
changedPos = true;
|
||||
}
|
||||
|
||||
if (mInitial) {
|
||||
// mCurPosition = mapPosition;
|
||||
mInitial = false;
|
||||
}
|
||||
|
||||
mTileX = tileX;
|
||||
mTileY = tileY;
|
||||
mPrevZoom = zoomLevel;
|
||||
|
||||
if (changedZoom) {
|
||||
// need to update visible list first when zoom level changes
|
||||
// as scaling is relative to the tiles of current zoom-level
|
||||
updateVisibleList(mapPosition, 0);
|
||||
} else {
|
||||
// pass new position to glThread
|
||||
MapRenderer.updatePosition(mapPosition);
|
||||
}
|
||||
|
||||
if (!MapView.debugFrameTime)
|
||||
mMapView.requestRender();
|
||||
|
||||
if (changedPos)
|
||||
updateVisibleList(mapPosition, zdir);
|
||||
|
||||
if (changedPos || changedZoom) {
|
||||
int remove = mTiles.size() - MapRenderer.CACHE_TILES;
|
||||
if (remove > 50)
|
||||
limitCache(mapPosition, remove);
|
||||
}
|
||||
|
||||
limitLoadQueue();
|
||||
|
||||
}
|
||||
|
||||
private static boolean updateVisibleList(MapPosition mapPosition, int zdir) {
|
||||
double x = mapPosition.x;
|
||||
double y = mapPosition.y;
|
||||
byte zoomLevel = mapPosition.zoomLevel;
|
||||
float scale = mapPosition.scale;
|
||||
|
||||
double add = 1.0f / scale;
|
||||
int offsetX = (int) ((mWidth >> 1) * add) + Tile.TILE_SIZE;
|
||||
int offsetY = (int) ((mHeight >> 1) * add) + Tile.TILE_SIZE;
|
||||
|
||||
long pixelRight = (long) x + offsetX;
|
||||
long pixelBottom = (long) y + offsetY;
|
||||
long pixelLeft = (long) x - offsetX;
|
||||
long pixelTop = (long) y - offsetY;
|
||||
|
||||
int tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel);
|
||||
int tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel);
|
||||
int tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel);
|
||||
int tileBottom = MercatorProjection.pixelYToTileY(pixelBottom, zoomLevel);
|
||||
|
||||
mJobList.clear();
|
||||
|
||||
// set non processed tiles to isLoading == false
|
||||
mMapView.addJobs(null);
|
||||
|
||||
int tiles = 0;
|
||||
if (newTiles == null)
|
||||
return false;
|
||||
|
||||
int max = newTiles.tiles.length - 1;
|
||||
|
||||
for (int yy = tileTop; yy <= tileBottom; yy++) {
|
||||
for (int xx = tileLeft; xx <= tileRight; xx++) {
|
||||
|
||||
if (tiles == max)
|
||||
break;
|
||||
|
||||
MapTile tile = QuadTree.getTile(xx, yy, zoomLevel);
|
||||
|
||||
if (tile == null) {
|
||||
tile = new MapTile(xx, yy, zoomLevel);
|
||||
|
||||
QuadTree.add(tile);
|
||||
mTiles.add(tile);
|
||||
}
|
||||
|
||||
newTiles.tiles[tiles++] = tile;
|
||||
|
||||
if (!(tile.isLoading || tile.newData || tile.isReady)) {
|
||||
mJobList.add(tile);
|
||||
}
|
||||
|
||||
if (zdir > 0 && zoomLevel > 0) {
|
||||
// prefetch parent
|
||||
MapTile parent = tile.rel.parent.tile;
|
||||
|
||||
if (parent == null) {
|
||||
parent = new MapTile(xx >> 1, yy >> 1, (byte) (zoomLevel - 1));
|
||||
|
||||
QuadTree.add(parent);
|
||||
mTiles.add(parent);
|
||||
}
|
||||
|
||||
if (!(parent.isLoading || parent.isReady || parent.newData)) {
|
||||
if (!mJobList.contains(parent))
|
||||
mJobList.add(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pass new tile list to glThread
|
||||
newTiles.cnt = tiles;
|
||||
newTiles = MapRenderer.updateTiles(mapPosition, newTiles);
|
||||
|
||||
if (mJobList.size() > 0) {
|
||||
updateTileDistances(mJobList, mapPosition);
|
||||
Collections.sort(mJobList);
|
||||
mMapView.addJobs(mJobList);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void clearTile(MapTile t) {
|
||||
t.newData = false;
|
||||
t.isLoading = false;
|
||||
t.isReady = false;
|
||||
|
||||
LineLayers.clear(t.lineLayers);
|
||||
PolygonLayers.clear(t.polygonLayers);
|
||||
|
||||
t.labels = null;
|
||||
t.lineLayers = null;
|
||||
t.polygonLayers = null;
|
||||
|
||||
if (t.vbo != null) {
|
||||
MapRenderer.addVBO(t.vbo);
|
||||
t.vbo = null;
|
||||
}
|
||||
|
||||
QuadTree.remove(t);
|
||||
}
|
||||
|
||||
private static boolean childIsActive(MapTile t) {
|
||||
MapTile c = null;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (t.rel.child[i] == null)
|
||||
continue;
|
||||
|
||||
c = t.rel.child[i].tile;
|
||||
if (c != null && c.isActive && !(c.isReady || c.newData))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME still the chance that one jumped two zoomlevels between
|
||||
// cur and draw. should use reference counter instead
|
||||
private static boolean tileInUse(MapTile t) {
|
||||
byte z = mPrevZoom;
|
||||
|
||||
if (t.isActive) {
|
||||
return true;
|
||||
|
||||
} else if (t.zoomLevel == z + 1) {
|
||||
MapTile p = t.rel.parent.tile;
|
||||
|
||||
if (p != null && p.isActive && !(p.isReady || p.newData))
|
||||
return true;
|
||||
|
||||
} else if (t.zoomLevel == z + 2) {
|
||||
MapTile p = t.rel.parent.parent.tile;
|
||||
|
||||
if (p != null && p.isActive && !(p.isReady || p.newData))
|
||||
return true;
|
||||
|
||||
} else if (t.zoomLevel == z - 1) {
|
||||
if (childIsActive(t))
|
||||
return true;
|
||||
|
||||
} else if (t.zoomLevel == z - 2) {
|
||||
for (QuadTree c : t.rel.child) {
|
||||
if (c == null)
|
||||
continue;
|
||||
|
||||
if (c.tile != null && childIsActive(c.tile))
|
||||
return true;
|
||||
}
|
||||
} else if (t.zoomLevel == z - 3) {
|
||||
for (QuadTree c : t.rel.child) {
|
||||
if (c == null)
|
||||
continue;
|
||||
|
||||
for (QuadTree c2 : c.child) {
|
||||
if (c2 == null)
|
||||
continue;
|
||||
|
||||
if (c2.tile != null && childIsActive(c2.tile))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void updateTileDistances(ArrayList<?> tiles,
|
||||
MapPosition mapPosition) {
|
||||
int h = (Tile.TILE_SIZE >> 1);
|
||||
byte zoom = mapPosition.zoomLevel;
|
||||
long x = (long) mapPosition.x;
|
||||
long y = (long) mapPosition.y;
|
||||
|
||||
int diff;
|
||||
long dx, dy;
|
||||
|
||||
// TODO this could need some fixing, and optimization
|
||||
// to consider move/zoom direction
|
||||
|
||||
for (int i = 0, n = tiles.size(); i < n; i++) {
|
||||
JobTile t = (JobTile) tiles.get(i);
|
||||
diff = (t.zoomLevel - zoom);
|
||||
|
||||
if (diff == 0) {
|
||||
dx = (t.pixelX + h) - x;
|
||||
dy = (t.pixelY + h) - y;
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.25f;
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f;
|
||||
} else if (diff > 0) {
|
||||
// tile zoom level is child of current
|
||||
|
||||
if (diff < 3) {
|
||||
dx = ((t.pixelX + h) >> diff) - x;
|
||||
dy = ((t.pixelY + h) >> diff) - y;
|
||||
}
|
||||
else {
|
||||
dx = ((t.pixelX + h) >> (diff >> 1)) - x;
|
||||
dy = ((t.pixelY + h) >> (diff >> 1)) - y;
|
||||
}
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy));
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy));
|
||||
|
||||
} else {
|
||||
// tile zoom level is parent of current
|
||||
dx = ((t.pixelX + h) << -diff) - x;
|
||||
dy = ((t.pixelY + h) << -diff) - y;
|
||||
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * (-diff * 0.5f);
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void limitCache(MapPosition mapPosition, int remove) {
|
||||
int removes = remove;
|
||||
|
||||
int size = mTiles.size();
|
||||
// int tmp = size;
|
||||
|
||||
// remove orphaned tiles
|
||||
for (int i = 0; i < size;) {
|
||||
MapTile cur = mTiles.get(i);
|
||||
// make sure tile cannot be used by GL or MapWorker Thread
|
||||
if ((!cur.isActive) && (!cur.isLoading) && (!cur.newData)
|
||||
&& (!cur.isReady) && (!tileInUse(cur))) {
|
||||
|
||||
clearTile(cur);
|
||||
mTiles.remove(i);
|
||||
removes--;
|
||||
size--;
|
||||
// Log.d(TAG, "remove empty tile" + cur);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Log.d(TAG, "remove tiles: " + removes + " " + size + " " + tmp);
|
||||
|
||||
if (removes <= 0)
|
||||
return;
|
||||
|
||||
updateTileDistances(mTiles, mapPosition);
|
||||
Collections.sort(mTiles);
|
||||
|
||||
// boolean printAll = false;
|
||||
|
||||
for (int i = 1; i <= removes; i++) {
|
||||
|
||||
MapTile t = mTiles.remove(size - i);
|
||||
|
||||
synchronized (t) {
|
||||
if (t.isActive) {
|
||||
// dont remove tile used by renderthread
|
||||
Log.d(TAG, "X not removing active " + t + " " + t.distance);
|
||||
|
||||
// if (printAll) {
|
||||
// printAll = false;
|
||||
// for (GLMapTile tt : mTiles)
|
||||
// Log.d(TAG, ">>>" + tt + " " + tt.distance);
|
||||
// }
|
||||
mTiles.add(t);
|
||||
} else if ((t.isReady || t.newData) && tileInUse(t)) {
|
||||
// check if this tile could be used as proxy
|
||||
Log.d(TAG, "X not removing proxy: " + t + " " + t.distance);
|
||||
mTiles.add(t);
|
||||
} else if (t.isLoading) {
|
||||
// FIXME !!! if we add tile back on next limit cache
|
||||
// this will be removed. clearTile could interfere with
|
||||
// MapGenerator... clear in passTile().
|
||||
Log.d(TAG, "X cancel loading " + t + " " + t.distance);
|
||||
t.isLoading = false;
|
||||
// mTiles.add(t);
|
||||
|
||||
} else {
|
||||
clearTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void limitLoadQueue() {
|
||||
int size = mTilesLoaded.size();
|
||||
|
||||
if (size < MAX_TILES_IN_QUEUE)
|
||||
return;
|
||||
|
||||
synchronized (mTilesLoaded) {
|
||||
|
||||
// remove uploaded tiles
|
||||
for (int i = 0; i < size;) {
|
||||
MapTile t = mTilesLoaded.get(i);
|
||||
// rel == null means tile is already removed by limitCache
|
||||
if (!t.newData || t.rel == null) {
|
||||
mTilesLoaded.remove(i);
|
||||
size--;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// clear loaded but not used tiles
|
||||
if (size < MAX_TILES_IN_QUEUE)
|
||||
return;
|
||||
// Log.d(TAG, "queue: " + mTilesLoaded.size() + " " + size + " "
|
||||
// + (size - MAX_TILES_IN_QUEUE / 2));
|
||||
|
||||
for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) {
|
||||
|
||||
MapTile t = mTilesLoaded.get(i);
|
||||
|
||||
synchronized (t) {
|
||||
if (t.rel == null) {
|
||||
mTilesLoaded.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tileInUse(t)) {
|
||||
// Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Log.d(TAG, "remove unused tile data: " + t);
|
||||
mTilesLoaded.remove(i);
|
||||
mTiles.remove(t);
|
||||
clearTile(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage tiles that have data to be uploaded to gl
|
||||
*
|
||||
* @param tile
|
||||
* tile processed by MapGenerator
|
||||
*/
|
||||
static void addTileLoaded(MapTile tile) {
|
||||
synchronized (mTilesLoaded) {
|
||||
mTilesLoaded.add(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/org/oscim/view/glrenderer/VertexBufferObject.java
Normal file
26
src/org/oscim/view/glrenderer/VertexBufferObject.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 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 License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
class VertexBufferObject {
|
||||
int id;
|
||||
int size;
|
||||
|
||||
VertexBufferObject(int id) {
|
||||
this.id = id;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
285
src/org/oscim/view/glrenderer/WayDecorator.java
Normal file
285
src/org/oscim/view/glrenderer/WayDecorator.java
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.glrenderer;
|
||||
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
import org.oscim.view.utils.GeometryUtils;
|
||||
|
||||
import android.util.FloatMath;
|
||||
|
||||
final class WayDecorator {
|
||||
// /**
|
||||
// * Minimum distance in pixels before the symbol is repeated.
|
||||
// */
|
||||
// private static final int DISTANCE_BETWEEN_SYMBOLS = 200;
|
||||
|
||||
// /**
|
||||
// * Minimum distance in pixels before the way name is repeated.
|
||||
// */
|
||||
// private static final int DISTANCE_BETWEEN_WAY_NAMES = 500;
|
||||
|
||||
// /**
|
||||
// * Distance in pixels to skip from both ends of a segment.
|
||||
// */
|
||||
// private static final int SEGMENT_SAFETY_DISTANCE = 30;
|
||||
|
||||
// static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter,
|
||||
// boolean repeatSymbol, float[][] coordinates,
|
||||
// List<SymbolContainer> waySymbols) {
|
||||
// int skipPixels = SEGMENT_SAFETY_DISTANCE;
|
||||
//
|
||||
// // get the first way point coordinates
|
||||
// float previousX = coordinates[0][0];
|
||||
// float previousY = coordinates[0][1];
|
||||
//
|
||||
// // draw the symbol on each way segment
|
||||
// float segmentLengthRemaining;
|
||||
// float segmentSkipPercentage;
|
||||
// float symbolAngle;
|
||||
// for (int i = 2; i < coordinates[0].length; i += 2) {
|
||||
// // get the current way point coordinates
|
||||
// float currentX = coordinates[0][i];
|
||||
// float currentY = coordinates[0][i + 1];
|
||||
//
|
||||
// // calculate the length of the current segment (Euclidian distance)
|
||||
// float diffX = currentX - previousX;
|
||||
// float diffY = currentY - previousY;
|
||||
// double segmentLengthInPixel = Math.sqrt(diffX * diffX + diffY * diffY);
|
||||
// segmentLengthRemaining = (float) segmentLengthInPixel;
|
||||
//
|
||||
// while (segmentLengthRemaining - skipPixels > SEGMENT_SAFETY_DISTANCE) {
|
||||
// // calculate the percentage of the current segment to skip
|
||||
// segmentSkipPercentage = skipPixels / segmentLengthRemaining;
|
||||
//
|
||||
// // move the previous point forward towards the current point
|
||||
// previousX += diffX * segmentSkipPercentage;
|
||||
// previousY += diffY * segmentSkipPercentage;
|
||||
// symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY,
|
||||
// currentX - previousX));
|
||||
//
|
||||
// waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY,
|
||||
// alignCenter, symbolAngle));
|
||||
//
|
||||
// // check if the symbol should only be rendered once
|
||||
// if (!repeatSymbol) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // recalculate the distances
|
||||
// diffX = currentX - previousX;
|
||||
// diffY = currentY - previousY;
|
||||
//
|
||||
// // recalculate the remaining length of the current segment
|
||||
// segmentLengthRemaining -= skipPixels;
|
||||
//
|
||||
// // set the amount of pixels to skip before repeating the symbol
|
||||
// skipPixels = DISTANCE_BETWEEN_SYMBOLS;
|
||||
// }
|
||||
//
|
||||
// skipPixels -= segmentLengthRemaining;
|
||||
// if (skipPixels < SEGMENT_SAFETY_DISTANCE) {
|
||||
// skipPixels = SEGMENT_SAFETY_DISTANCE;
|
||||
// }
|
||||
//
|
||||
// // set the previous way point coordinates for the next loop
|
||||
// previousX = currentX;
|
||||
// previousY = currentY;
|
||||
// }
|
||||
// }
|
||||
|
||||
static TextItem renderText(float[] coordinates, String text, PathText pathText,
|
||||
int pos, int len, TextItem textItems) {
|
||||
TextItem items = textItems;
|
||||
TextItem t = null;
|
||||
// calculate the way name length plus some margin of safety
|
||||
float wayNameWidth = -1;
|
||||
float minWidth = 100;
|
||||
int skipPixels = 0;
|
||||
|
||||
// get the first way point coordinates
|
||||
int previousX = (int) coordinates[pos + 0];
|
||||
int previousY = (int) coordinates[pos + 1];
|
||||
|
||||
// find way segments long enough to draw the way name on them
|
||||
for (int i = pos + 2; i < pos + len; i += 2) {
|
||||
// get the current way point coordinates
|
||||
int currentX = (int) coordinates[i];
|
||||
int currentY = (int) coordinates[i + 1];
|
||||
|
||||
// calculate the length of the current segment (Euclidian distance)
|
||||
float diffX = currentX - previousX;
|
||||
float diffY = currentY - previousY;
|
||||
|
||||
for (int j = i + 2; j < pos + len; j += 2) {
|
||||
int nextX = (int) coordinates[j];
|
||||
int nextY = (int) coordinates[j + 1];
|
||||
|
||||
if (diffY == 0) {
|
||||
if ((currentY - nextY) != 0)
|
||||
break;
|
||||
|
||||
currentX = nextX;
|
||||
currentY = nextY;
|
||||
continue;
|
||||
} else if ((currentY - nextY) == 0)
|
||||
break;
|
||||
|
||||
float diff = ((diffX) / (diffY) - (float) (currentX - nextX)
|
||||
/ (currentY - nextY));
|
||||
|
||||
// skip segments with corners
|
||||
if (diff >= 0.1f || diff <= -0.1f)
|
||||
break;
|
||||
|
||||
currentX = nextX;
|
||||
currentY = nextY;
|
||||
}
|
||||
|
||||
diffX = currentX - previousX;
|
||||
diffY = currentY - previousY;
|
||||
|
||||
if (diffX < 0)
|
||||
diffX = -diffX;
|
||||
if (diffY < 0)
|
||||
diffY = -diffY;
|
||||
|
||||
if (diffX + diffY < minWidth) {
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wayNameWidth > 0 && diffX + diffY < wayNameWidth) {
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
continue;
|
||||
}
|
||||
|
||||
float segmentLengthInPixel = FloatMath.sqrt(diffX * diffX + diffY * diffY);
|
||||
|
||||
if (skipPixels > 0) {
|
||||
skipPixels -= segmentLengthInPixel;
|
||||
|
||||
} else if (segmentLengthInPixel > minWidth) {
|
||||
|
||||
if (wayNameWidth < 0) {
|
||||
wayNameWidth = pathText.paint.measureText(text);
|
||||
}
|
||||
|
||||
if (segmentLengthInPixel > wayNameWidth + 25) {
|
||||
|
||||
float s = (wayNameWidth + 25) / segmentLengthInPixel;
|
||||
int width, height;
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
if (previousX < currentX) {
|
||||
x1 = previousX;
|
||||
y1 = previousY;
|
||||
x2 = currentX;
|
||||
y2 = currentY;
|
||||
} else {
|
||||
x1 = currentX;
|
||||
y1 = currentY;
|
||||
x2 = previousX;
|
||||
y2 = previousY;
|
||||
}
|
||||
|
||||
// estimate position of text on path
|
||||
width = (x2 - x1) / 2;
|
||||
x2 = x2 - (int) (width - s * width);
|
||||
x1 = x1 + (int) (width - s * width);
|
||||
|
||||
height = (y2 - y1) / 2;
|
||||
y2 = y2 - (int) (height - s * height);
|
||||
y1 = y1 + (int) (height - s * height);
|
||||
|
||||
short top = (short) (y1 < y2 ? y1 : y2);
|
||||
short bot = (short) (y1 < y2 ? y2 : y1);
|
||||
|
||||
boolean intersects = false;
|
||||
|
||||
for (TextItem t2 = items; t2 != null; t2 = t2.next) {
|
||||
|
||||
// check crossings
|
||||
if (GeometryUtils.lineIntersect(x1, y1, x2, y2, t2.x1, t2.y1,
|
||||
t2.x2, t2.y2)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// check overlapping labels of road with more than one
|
||||
// way
|
||||
short top2 = (t2.y1 < t2.y2 ? t2.y1 : t2.y2);
|
||||
short bot2 = (t2.y1 < t2.y2 ? t2.y2 : t2.y1);
|
||||
|
||||
if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2
|
||||
&& top2 - 10 < bot) {
|
||||
|
||||
if (t2.text.equals(text)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (intersects) {
|
||||
previousX = (int) coordinates[pos + i];
|
||||
previousY = (int) coordinates[pos + i + 1];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Log.d("mapsforge", "add " + text + " " + first + " " + last);
|
||||
|
||||
if (previousX < currentX) {
|
||||
x1 = previousX;
|
||||
y1 = previousY;
|
||||
x2 = currentX;
|
||||
y2 = currentY;
|
||||
} else {
|
||||
x1 = currentX;
|
||||
y1 = currentY;
|
||||
x2 = previousX;
|
||||
y2 = previousY;
|
||||
}
|
||||
|
||||
// if (t == null)
|
||||
t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, text,
|
||||
pathText, wayNameWidth);
|
||||
|
||||
t.x1 = (short) x1;
|
||||
t.y1 = (short) y1;
|
||||
t.x2 = (short) x2;
|
||||
t.y2 = (short) y2;
|
||||
|
||||
t.next = items;
|
||||
items = t;
|
||||
|
||||
// skipPixels = DISTANCE_BETWEEN_WAY_NAMES;
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
// store the previous way point coordinates
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private WayDecorator() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
47
src/org/oscim/view/mapgenerator/IMapGenerator.java
Normal file
47
src/org/oscim/view/mapgenerator/IMapGenerator.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.oscim.view.mapgenerator;
|
||||
|
||||
import org.oscim.database.IMapDatabase;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
|
||||
/**
|
||||
* A MapGenerator provides map tiles either by downloading or rendering them.
|
||||
*/
|
||||
public interface IMapGenerator {
|
||||
/**
|
||||
* Called once at the end of the MapGenerator lifecycle.
|
||||
*/
|
||||
void cleanup();
|
||||
|
||||
/**
|
||||
* Called when a job needs to be executed.
|
||||
*
|
||||
* @param tile
|
||||
* the job that should be executed.
|
||||
* @return true if the job was executed successfully, false otherwise.
|
||||
*/
|
||||
boolean executeJob(JobTile tile);
|
||||
|
||||
/**
|
||||
* @param mapDatabase
|
||||
* the MapDatabase from which the map data will be read.
|
||||
*/
|
||||
void setMapDatabase(IMapDatabase mapDatabase);
|
||||
|
||||
IMapDatabase getMapDatabase();
|
||||
|
||||
void setRenderTheme(RenderTheme theme);
|
||||
}
|
||||
78
src/org/oscim/view/mapgenerator/JobQueue.java
Normal file
78
src/org/oscim/view/mapgenerator/JobQueue.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.oscim.view.mapgenerator;
|
||||
|
||||
//import static org.oscim.view.mapgenerator.JobTile.LOADING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* A JobQueue keeps the list of pending jobs for a MapView and prioritizes them.
|
||||
*/
|
||||
public class JobQueue {
|
||||
private static final int INITIAL_CAPACITY = 64;
|
||||
|
||||
private PriorityQueue<JobTile> mPriorityQueue;
|
||||
|
||||
/**
|
||||
*/
|
||||
public JobQueue() {
|
||||
mPriorityQueue = new PriorityQueue<JobTile>(INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tiles
|
||||
* the job to be added to this queue.
|
||||
*/
|
||||
public synchronized void setJobs(ArrayList<JobTile> tiles) {
|
||||
mPriorityQueue.clear();
|
||||
// mPriorityQueue.addAll(tiles);
|
||||
for (int i = 0, n = tiles.size(); i < n; i++) {
|
||||
JobTile tile = tiles.get(i);
|
||||
// tile.state = LOADING;
|
||||
tile.isLoading = true;
|
||||
mPriorityQueue.offer(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all jobs from this queue.
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
for (int i = 0, n = mPriorityQueue.size(); i < n; i++) {
|
||||
JobTile tile = mPriorityQueue.poll();
|
||||
// tile.state = 0;
|
||||
tile.isLoading = false;
|
||||
}
|
||||
mPriorityQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this queue contains no jobs, false otherwise.
|
||||
*/
|
||||
public synchronized boolean isEmpty() {
|
||||
return mPriorityQueue.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the most important job from this queue or null, if empty.
|
||||
*/
|
||||
public synchronized JobTile poll() {
|
||||
JobTile tile = mPriorityQueue.poll();
|
||||
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
62
src/org/oscim/view/mapgenerator/JobTile.java
Normal file
62
src/org/oscim/view/mapgenerator/JobTile.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.mapgenerator;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class JobTile extends Tile implements Comparable<JobTile> {
|
||||
// public final static int LOADING = 1;
|
||||
// public final static int NEWDATA = 1 << 1;
|
||||
// public final static int READY = 1 << 2;
|
||||
// public final static int AVAILABLE = 1 << 1 | 1 << 2;
|
||||
// public final static int CANCELED = 1 << 3;
|
||||
// public int state;
|
||||
|
||||
/**
|
||||
* tile is in JobQueue
|
||||
*/
|
||||
public boolean isLoading;
|
||||
|
||||
/**
|
||||
* distance from map center.
|
||||
*/
|
||||
public float distance;
|
||||
|
||||
/**
|
||||
* @param tileX
|
||||
* ...
|
||||
* @param tileY
|
||||
* ...
|
||||
* @param zoomLevel
|
||||
* ..
|
||||
*/
|
||||
public JobTile(int tileX, int tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(JobTile o) {
|
||||
if (this.distance < o.distance) {
|
||||
return -1;
|
||||
}
|
||||
if (this.distance > o.distance) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
97
src/org/oscim/view/mapgenerator/MapWorker.java
Normal file
97
src/org/oscim/view/mapgenerator/MapWorker.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.oscim.view.mapgenerator;
|
||||
|
||||
import org.oscim.view.IMapRenderer;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.utils.PausableThread;
|
||||
|
||||
/**
|
||||
* A MapWorker uses a {@link IMapGenerator} to generate map tiles. It runs in a separate thread to avoid blocking the UI
|
||||
* thread.
|
||||
*/
|
||||
public class MapWorker extends PausableThread {
|
||||
private final String THREAD_NAME;
|
||||
private final JobQueue mJobQueue;
|
||||
private final IMapGenerator mMapGenerator;
|
||||
private final IMapRenderer mMapRenderer;
|
||||
|
||||
// private final int mPrio;
|
||||
|
||||
/**
|
||||
* @param id
|
||||
* thread id
|
||||
* @param mapView
|
||||
* the MapView for which this MapWorker generates map tiles.
|
||||
* @param mapGenerator
|
||||
* ...
|
||||
* @param mapRenderer
|
||||
* ...
|
||||
*/
|
||||
public MapWorker(int id, MapView mapView, IMapGenerator mapGenerator,
|
||||
IMapRenderer mapRenderer) {
|
||||
super();
|
||||
mJobQueue = mapView.getJobQueue();
|
||||
mMapGenerator = mapGenerator;
|
||||
mMapRenderer = mapRenderer;
|
||||
|
||||
THREAD_NAME = "MapWorker" + id;
|
||||
// mPrio = Math.max(Thread.MIN_PRIORITY + id, Thread.NORM_PRIORITY - 1);
|
||||
}
|
||||
|
||||
public IMapGenerator getMapGenerator() {
|
||||
return mMapGenerator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterRun() {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWork() {
|
||||
JobTile tile = mJobQueue.poll();
|
||||
|
||||
if (mMapGenerator == null || tile == null)
|
||||
return;
|
||||
|
||||
boolean success = mMapGenerator.executeJob(tile);
|
||||
|
||||
if (!isInterrupted() && success) {
|
||||
mMapRenderer.passTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getThreadName() {
|
||||
return THREAD_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void takeabreak() {
|
||||
mMapGenerator.getMapDatabase().cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getThreadPriority() {
|
||||
return (Thread.NORM_PRIORITY + Thread.MIN_PRIORITY) / 3;
|
||||
// return mPrio;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasWork() {
|
||||
return !mJobQueue.isEmpty();
|
||||
}
|
||||
}
|
||||
32
src/org/oscim/view/mapgenerator/TileDistanceSort.java
Normal file
32
src/org/oscim/view/mapgenerator/TileDistanceSort.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.oscim.view.mapgenerator;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TileDistanceSort implements Comparator<JobTile> {
|
||||
|
||||
@Override
|
||||
public int compare(JobTile tile1, JobTile tile2) {
|
||||
if (tile1.distance == tile2.distance)
|
||||
return 0;
|
||||
|
||||
return tile1.distance > tile2.distance ? 1 : -1;
|
||||
}
|
||||
}
|
||||
35
src/org/oscim/view/overlay/Overlay.java
Normal file
35
src/org/oscim/view/overlay/Overlay.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.oscim.view.overlay;
|
||||
|
||||
public class Overlay {
|
||||
|
||||
synchronized void move() {
|
||||
|
||||
}
|
||||
|
||||
synchronized void addBitmap() {
|
||||
|
||||
}
|
||||
|
||||
synchronized boolean onTouch() {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized void draw() {
|
||||
|
||||
}
|
||||
}
|
||||
19
src/org/oscim/view/overlay/TiledOverlay.java
Normal file
19
src/org/oscim/view/overlay/TiledOverlay.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.oscim.view.overlay;
|
||||
|
||||
public class TiledOverlay extends Overlay {
|
||||
|
||||
}
|
||||
254
src/org/oscim/view/swrenderer/CanvasRasterer.java
Normal file
254
src/org/oscim/view/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.oscim.view.swrenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.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/oscim/view/swrenderer/CircleContainer.java
Normal file
33
src/org/oscim/view/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.oscim.view.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;
|
||||
}
|
||||
}
|
||||
985
src/org/oscim/view/swrenderer/DependencyCache.java
Normal file
985
src/org/oscim/view/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.oscim.view.swrenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.oscim.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/org/oscim/view/swrenderer/ImmutablePoint.java
Normal file
116
src/org/oscim/view/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.oscim.view.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/oscim/view/swrenderer/LabelPlacement.java
Normal file
766
src/org/oscim/view/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.oscim.view.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.oscim.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/oscim/view/swrenderer/LayerContainer.java
Normal file
63
src/org/oscim/view/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.oscim.view.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/oscim/view/swrenderer/LevelContainer.java
Normal file
45
src/org/oscim/view/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.oscim.view.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;
|
||||
}
|
||||
}
|
||||
541
src/org/oscim/view/swrenderer/MapGenerator.java
Normal file
541
src/org/oscim/view/swrenderer/MapGenerator.java
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* 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.oscim.view.swrenderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.Tag;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.database.IMapDatabase;
|
||||
import org.oscim.database.IMapDatabaseCallback;
|
||||
import org.oscim.database.mapfile.MapDatabase;
|
||||
import org.oscim.theme.IRenderCallback;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.renderinstruction.Area;
|
||||
import org.oscim.theme.renderinstruction.Caption;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.theme.renderinstruction.PathText;
|
||||
import org.oscim.view.DebugSettings;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.FloatMath;
|
||||
|
||||
/**
|
||||
* A DatabaseRenderer renders map tiles by reading from a {@link MapDatabase}.
|
||||
*/
|
||||
public class MapGenerator implements IMapGenerator, IRenderCallback,
|
||||
IMapDatabaseCallback {
|
||||
// private static String TAG = MapGenerator.class.getName();
|
||||
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 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 Theme 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 JobTile mCurrentTile;
|
||||
private static long mCurrentTileY;
|
||||
private static long mCurrentTileX;
|
||||
private static long mCurrentTileZoom;
|
||||
|
||||
private float[] mCoords = null;
|
||||
|
||||
// private long _renderTime;
|
||||
private int _nodes, _nodesDropped;
|
||||
private MapView mMapView;
|
||||
|
||||
/**
|
||||
* Constructs a new DatabaseRenderer.
|
||||
*
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
public MapGenerator(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
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 (MapGenerator.renderTheme != null) {
|
||||
MapGenerator.renderTheme.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeJob(JobTile jobTile) {
|
||||
long time_load = System.currentTimeMillis();
|
||||
_nodes = 0;
|
||||
_nodesDropped = 0;
|
||||
// _renderTime = 0;
|
||||
|
||||
mCurrentTile = jobTile;
|
||||
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();
|
||||
|
||||
// Theme theme = mapGeneratorJob.jobParameters.theme;
|
||||
// if (!theme.equals(mPreviousJobTheme)) {
|
||||
// // if (MapGenerator.renderTheme == null)
|
||||
// // MapGenerator.renderTheme = getRenderTheme(theme);
|
||||
// // if (MapGenerator.renderTheme == null) {
|
||||
// // mPreviousJobTheme = null;
|
||||
// // return false;
|
||||
// // }
|
||||
// createWayLists();
|
||||
// mPreviousJobTheme = theme;
|
||||
// mPreviousZoomLevel = Byte.MIN_VALUE;
|
||||
// }
|
||||
//
|
||||
// byte zoomLevel = mCurrentTile.zoomLevel;
|
||||
// if (zoomLevel != mPreviousZoomLevel) {
|
||||
// setScaleStrokeWidth(zoomLevel);
|
||||
// mPreviousZoomLevel = zoomLevel;
|
||||
// }
|
||||
//
|
||||
// float textScale = mapGeneratorJob.jobParameters.textScale;
|
||||
// if (textScale != mPreviousTextScale) {
|
||||
// MapGenerator.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(MapGenerator.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;
|
||||
|
||||
DebugSettings debugSettings = mMapView.getDebugSettings();
|
||||
|
||||
if (debugSettings.mDrawTileFrames) {
|
||||
mCanvasRasterer.drawTileFrame();
|
||||
}
|
||||
|
||||
if (debugSettings.mDrawTileCoordinates) {
|
||||
mCanvasRasterer.drawTileCoordinates(mCurrentTile, time_load, time_draw,
|
||||
_nodes, _nodesDropped);
|
||||
}
|
||||
|
||||
clearLists();
|
||||
|
||||
// mapGeneratorJob.setBitmap(mTileBitmap);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAreaCaption(Caption caption) {
|
||||
// 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, float latitude, float longitude,
|
||||
Tag[] tags) {
|
||||
mDrawingLayer = mWays[getValidLayer(layer)];
|
||||
mPoiX = scaleLongitude(longitude);
|
||||
mPoiY = scaleLatitude(latitude);
|
||||
MapGenerator.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPointOfInterestCaption(Caption caption) {
|
||||
// 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, short[] 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, int level) {
|
||||
// List<ShapeContainer> c = mDrawingLayer.add(level, mWayDataContainer,
|
||||
// line.paint);
|
||||
//
|
||||
// if (mCurLevelContainer1 == null)
|
||||
// mCurLevelContainer1 = c;
|
||||
// else if (mCurLevelContainer2 == null)
|
||||
// mCurLevelContainer2 = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderArea(Area area, int level) {
|
||||
// if (area.paintFill != null)
|
||||
// mCurLevelContainer1 = mDrawingLayer.add(level, mWayDataContainer,
|
||||
// area.paintFill);
|
||||
// if (area.paintOutline != null)
|
||||
// mCurLevelContainer1 = mDrawingLayer.add(level, mWayDataContainer,
|
||||
// area.paintOutline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWaySymbol(Bitmap symbolBitmap, boolean alignCenter,
|
||||
boolean repeatSymbol) {
|
||||
// WayDecorator.renderSymbol(symbolBitmap, alignCenter, repeatSymbol,
|
||||
// coordinates,
|
||||
// waySymbols);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderWayText(PathText pathText) {
|
||||
// if (mWayDataContainer.textPos[0] >= 0)
|
||||
// WayDecorator.renderText(this, paint, outline, mCoords, mWayDataContainer, mWayNames);
|
||||
}
|
||||
|
||||
@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 = MapGenerator.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);
|
||||
// MapGenerator.renderTheme.scaleStrokeWidth((float) Math.pow(STROKE_INCREASE,
|
||||
// zoomLevelDiff));
|
||||
// }
|
||||
|
||||
@Override
|
||||
public IMapDatabase getMapDatabase() {
|
||||
return mMapDatabase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderTheme(RenderTheme theme) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkWay(Tag[] tags, boolean closed) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
}
|
||||
583
src/org/oscim/view/swrenderer/MapRenderer.java
Normal file
583
src/org/oscim/view/swrenderer/MapRenderer.java
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* 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.oscim.view.swrenderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.mapgenerator.IMapGenerator;
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
import org.oscim.view.mapgenerator.TileDistanceSort;
|
||||
import org.oscim.view.utils.GlUtils;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MapRenderer implements org.oscim.view.IMapRenderer {
|
||||
// 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<JobTile> mJobList;
|
||||
|
||||
ArrayList<Integer> mTextures;
|
||||
MapView mMapView;
|
||||
|
||||
MapTile[] currentTiles;
|
||||
MapTile[] newTiles;
|
||||
int currentTileCnt = 0;
|
||||
|
||||
// private TileCacheKey mTileCacheKey;
|
||||
// private LinkedHashMap<TileCacheKey, GLMapTile> mTiles;
|
||||
private ArrayList<MapTile> 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;
|
||||
// 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<JobTile>();
|
||||
|
||||
// mTiles = new LinkedHashMap<TileCacheKey, GLMapTile>(100);
|
||||
mTileList = new ArrayList<MapTile>();
|
||||
|
||||
// mTileCacheKey = new TileCacheKey();
|
||||
mInitial = true;
|
||||
}
|
||||
|
||||
private void limitCache(byte zoom, int remove) {
|
||||
long x = mTileX;
|
||||
long y = mTileY;
|
||||
int diff;
|
||||
|
||||
for (MapTile 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++) {
|
||||
MapTile 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(Integer.valueOf(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;
|
||||
|
||||
int tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel);
|
||||
int tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel);
|
||||
int tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel);
|
||||
int tileBottom = MercatorProjection.pixelYToTileY(pixelBottom, zoomLevel);
|
||||
|
||||
mJobList.clear();
|
||||
|
||||
// IMapGenerator mapGenerator = mMapView.getMapGenerator();
|
||||
|
||||
int tiles = 0;
|
||||
for (int tileY = tileTop - 1; tileY <= tileBottom + 1; tileY++) {
|
||||
for (int 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.isReady || (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, mJobParameter,
|
||||
// mDebugSettings);
|
||||
// job.setScale(scale);
|
||||
// mJobList.add(job);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
|
||||
limitCache(zoomLevel, (mTileList.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.addJobs(mJobList);
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public synchronized void updateMap(boolean clear) {
|
||||
|
||||
boolean update = false;
|
||||
|
||||
mMapPosition = mMapView.getMapPosition().getMapPosition();
|
||||
|
||||
long x = (long) MercatorProjection.longitudeToPixelX(mMapPosition);
|
||||
long y = (long) MercatorProjection.latitudeToPixelY(mMapPosition);
|
||||
|
||||
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(JobTile jobTile) {
|
||||
|
||||
// mMapGeneratorJob = mapGeneratorJob;
|
||||
processedTile = false;
|
||||
mMapView.requestRender();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean drawTile(MapTile 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);
|
||||
|
||||
MapTile 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.isReady = 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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@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 MapTile[tiles];
|
||||
newTiles = new MapTile[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;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public IMapGenerator createMapGenerator() {
|
||||
return new MapGenerator(mMapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderTheme(RenderTheme t) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
84
src/org/oscim/view/swrenderer/MapTile.java
Normal file
84
src/org/oscim/view/swrenderer/MapTile.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view.swrenderer;
|
||||
|
||||
import org.oscim.view.mapgenerator.JobTile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class MapTile extends JobTile {
|
||||
private float mScale;
|
||||
|
||||
final MapTile[] child = { null, null, null, null };
|
||||
MapTile parent;
|
||||
|
||||
// private long mLoadTime;
|
||||
private int mTextureID;
|
||||
|
||||
boolean isVisible;
|
||||
|
||||
/**
|
||||
* @param tileX
|
||||
* ...
|
||||
* @param tileY
|
||||
* ...
|
||||
* @param zoomLevel
|
||||
* ..
|
||||
*/
|
||||
public MapTile(int tileX, int tileY, byte zoomLevel) {
|
||||
super(tileX, tileY, zoomLevel);
|
||||
mScale = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
115
src/org/oscim/view/swrenderer/PointTextContainer.java
Normal file
115
src/org/oscim/view/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.oscim.view.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/oscim/view/swrenderer/ShapeContainer.java
Normal file
19
src/org/oscim/view/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.oscim.view.swrenderer;
|
||||
|
||||
interface ShapeContainer {
|
||||
ShapeType getShapeType();
|
||||
}
|
||||
29
src/org/oscim/view/swrenderer/ShapePaintContainer.java
Normal file
29
src/org/oscim/view/swrenderer/ShapePaintContainer.java
Normal file
@@ -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.oscim.view.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/oscim/view/swrenderer/ShapeType.java
Normal file
19
src/org/oscim/view/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.oscim.view.swrenderer;
|
||||
|
||||
enum ShapeType {
|
||||
CIRCLE, WAY;
|
||||
}
|
||||
61
src/org/oscim/view/swrenderer/SymbolContainer.java
Normal file
61
src/org/oscim/view/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.oscim.view.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/oscim/view/swrenderer/WayDataContainer.java
Normal file
34
src/org/oscim/view/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.oscim.view.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;
|
||||
}
|
||||
}
|
||||
300
src/org/oscim/view/swrenderer/WayDecorator.java
Normal file
300
src/org/oscim/view/swrenderer/WayDecorator.java
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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.oscim.view.swrenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.view.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(MapGenerator mapGenerator, 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 = mapGenerator.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 text on path
|
||||
width = (x2 - x1) / 2;
|
||||
x2 = x2 - (int) (width - s * width);
|
||||
x1 = x1 + (int) (width - s * width);
|
||||
|
||||
height = (y2 - y1) / 2;
|
||||
y2 = y2 - (int) (height - s * height);
|
||||
y1 = y1 + (int) (height - s * height);
|
||||
|
||||
short top = (short) (y1 < y2 ? y1 : y2);
|
||||
short bot = (short) (y1 < y2 ? y2 : y1);
|
||||
|
||||
boolean intersects = false;
|
||||
|
||||
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/oscim/view/swrenderer/WayTextContainer.java
Normal file
37
src/org/oscim/view/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.oscim.view.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;
|
||||
}
|
||||
}
|
||||
52
src/org/oscim/view/utils/AndroidUtils.java
Normal file
52
src/org/oscim/view/utils/AndroidUtils.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.oscim.view.utils;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* A utility class with Android-specific helper methods.
|
||||
*/
|
||||
public final class AndroidUtils {
|
||||
/**
|
||||
* Build names to detect the emulator from the Android SDK.
|
||||
*/
|
||||
private static final String[] EMULATOR_NAMES = { "google_sdk", "sdk" };
|
||||
|
||||
/**
|
||||
* @return true if the application is running on the Android emulator, false otherwise.
|
||||
*/
|
||||
public static boolean applicationRunsOnAndroidEmulator() {
|
||||
for (int i = 0, n = EMULATOR_NAMES.length; i < n; ++i) {
|
||||
if (Build.PRODUCT.equals(EMULATOR_NAMES[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the current thread is the UI thread, false otherwise.
|
||||
*/
|
||||
public static boolean currentThreadIsUiThread() {
|
||||
return Looper.getMainLooper().getThread() == Thread.currentThread();
|
||||
}
|
||||
|
||||
private AndroidUtils() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
278
src/org/oscim/view/utils/GeometryUtils.java
Normal file
278
src/org/oscim/view/utils/GeometryUtils.java
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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.oscim.view.utils;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public final class GeometryUtils {
|
||||
/**
|
||||
* Calculates the center of the minimum bounding rectangle for the given coordinates.
|
||||
*
|
||||
* @param coordinates
|
||||
* the coordinates for which calculation should be done.
|
||||
* @return the center coordinates of the minimum bounding rectangle.
|
||||
*/
|
||||
static float[] calculateCenterOfBoundingBox(float[] coordinates) {
|
||||
float longitudeMin = coordinates[0];
|
||||
float longitudeMax = coordinates[0];
|
||||
float latitudeMax = coordinates[1];
|
||||
float latitudeMin = coordinates[1];
|
||||
|
||||
for (int i = 2; i < coordinates.length; i += 2) {
|
||||
if (coordinates[i] < longitudeMin) {
|
||||
longitudeMin = coordinates[i];
|
||||
} else if (coordinates[i] > longitudeMax) {
|
||||
longitudeMax = coordinates[i];
|
||||
}
|
||||
|
||||
if (coordinates[i + 1] < latitudeMin) {
|
||||
latitudeMin = coordinates[i + 1];
|
||||
} else if (coordinates[i + 1] > latitudeMax) {
|
||||
latitudeMax = coordinates[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return new float[] { (longitudeMin + longitudeMax) / 2, (latitudeMax + latitudeMin) / 2 };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param way
|
||||
* the coordinates of the way.
|
||||
* @return true if the given way is closed, false otherwise.
|
||||
*/
|
||||
static boolean isClosedWay(float[] way) {
|
||||
return Float.compare(way[0], way[way.length - 2]) == 0 && Float.compare(way[1], way[way.length - 1]) == 0;
|
||||
}
|
||||
|
||||
private GeometryUtils() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
static boolean linesIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
|
||||
// Return false if either of the lines have zero length
|
||||
if (x1 == x2 && y1 == y2 || x3 == x4 && y3 == y4) {
|
||||
return false;
|
||||
}
|
||||
// Fastest method, based on Franklin Antonio's
|
||||
// "Faster Line Segment Intersection" topic "in Graphics Gems III" book
|
||||
// (http://www.graphicsgems.org/)
|
||||
double ax = x2 - x1;
|
||||
double ay = y2 - y1;
|
||||
double bx = x3 - x4;
|
||||
double by = y3 - y4;
|
||||
double cx = x1 - x3;
|
||||
double cy = y1 - y3;
|
||||
|
||||
double alphaNumerator = by * cx - bx * cy;
|
||||
double commonDenominator = ay * bx - ax * by;
|
||||
|
||||
if (commonDenominator > 0) {
|
||||
if (alphaNumerator < 0 || alphaNumerator > commonDenominator) {
|
||||
return false;
|
||||
}
|
||||
} else if (commonDenominator < 0) {
|
||||
if (alphaNumerator > 0 || alphaNumerator < commonDenominator) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
double betaNumerator = ax * cy - ay * cx;
|
||||
if (commonDenominator > 0) {
|
||||
if (betaNumerator < 0 || betaNumerator > commonDenominator) {
|
||||
return false;
|
||||
}
|
||||
} else if (commonDenominator < 0) {
|
||||
if (betaNumerator > 0 || betaNumerator < commonDenominator) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (commonDenominator == 0) {
|
||||
// This code wasn't in Franklin Antonio's method. It was added by Keith
|
||||
// Woodward.
|
||||
// The lines are parallel.
|
||||
// Check if they're collinear.
|
||||
double y3LessY1 = y3 - y1;
|
||||
double collinearityTestForP3 = x1 * (y2 - y3) + x2 * (y3LessY1) + x3 * (y1 - y2); // see
|
||||
// http://mathworld.wolfram.com/Collinear.html
|
||||
// If p3 is collinear with p1 and p2 then p4 will also be collinear,
|
||||
// since p1-p2 is parallel with p3-p4
|
||||
if (collinearityTestForP3 == 0) {
|
||||
// The lines are collinear. Now check if they overlap.
|
||||
if (x1 >= x3 && x1 <= x4 || x1 <= x3 && x1 >= x4 || x2 >= x3 && x2 <= x4 || x2 <= x3 && x2 >= x4
|
||||
|| x3 >= x1 && x3 <= x2 || x3 <= x1 && x3 >= x2) {
|
||||
if (y1 >= y3 && y1 <= y4 || y1 <= y3 && y1 >= y4 || y2 >= y3 && y2 <= y4 || y2 <= y3 && y2 >= y4
|
||||
|| y3 >= y1 && y3 <= y2 || y3 <= y1 && y3 >= y2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean doesIntersect(double l1x1, double l1y1, double l1x2, double l1y2, double l2x1, double l2y1,
|
||||
double l2x2, double l2y2) {
|
||||
double denom = ((l2y2 - l2y1) * (l1x2 - l1x1)) - ((l2x2 - l2x1) * (l1y2 - l1y1));
|
||||
|
||||
if (denom == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double ua = (((l2x2 - l2x1) * (l1y1 - l2y1)) - ((l2y2 - l2y1) * (l1x1 - l2x1))) / denom;
|
||||
double ub = (((l1x2 - l1x1) * (l1y1 - l2y1)) - ((l1y2 - l1y1) * (l1x1 - l2x1))) / denom;
|
||||
|
||||
return ((ua >= 0.0d) && (ua <= 1.0d) && (ub >= 0.0d) && (ub <= 1.0d));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x1
|
||||
* ...
|
||||
* @param y1
|
||||
* ...
|
||||
* @param x2
|
||||
* ...
|
||||
* @param y2
|
||||
* ...
|
||||
* @param x3
|
||||
* ...
|
||||
* @param y3
|
||||
* ...
|
||||
* @param x4
|
||||
* ...
|
||||
* @param y4
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public static boolean lineIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
|
||||
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
|
||||
if (denom == 0.0) { // Lines are parallel.
|
||||
return false;
|
||||
}
|
||||
double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
|
||||
double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
|
||||
if (ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f) {
|
||||
// Get the intersection point.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// private static final int OUT_LEFT = 1;
|
||||
// private static final int OUT_TOP = 2;
|
||||
// private static final int OUT_RIGHT = 4;
|
||||
// private static final int OUT_BOTTOM = 8;
|
||||
//
|
||||
//
|
||||
// private static int outcode(double x, double y) {
|
||||
// /*
|
||||
// * Note on casts to double below. If the arithmetic of
|
||||
// * x+w or y+h is done in float, then some bits may be
|
||||
// * lost if the binary exponents of x/y and w/h are not
|
||||
// * similar. By converting to double before the addition
|
||||
// * we force the addition to be carried out in double to
|
||||
// * avoid rounding error in the comparison.
|
||||
// *
|
||||
// * See bug 4320890 for problems that this inaccuracy causes.
|
||||
// */
|
||||
// int out = 0;
|
||||
// if (this.width <= 0) {
|
||||
// out |= OUT_LEFT | OUT_RIGHT;
|
||||
// } else if (x < this.x) {
|
||||
// out |= OUT_LEFT;
|
||||
// } else if (x > this.x + (double) this.width) {
|
||||
// out |= OUT_RIGHT;
|
||||
// }
|
||||
// if (this.height <= 0) {
|
||||
// out |= OUT_TOP | OUT_BOTTOM;
|
||||
// } else if (y < this.y) {
|
||||
// out |= OUT_TOP;
|
||||
// } else if (y > this.y + (double) this.height) {
|
||||
// out |= OUT_BOTTOM;
|
||||
// }
|
||||
// return out;
|
||||
// }
|
||||
|
||||
// from http://shamimkhaliq.50megs.com/Java/lineclipper.htm
|
||||
// private static int outCodes(Point P)
|
||||
// {
|
||||
// int Code = 0;
|
||||
//
|
||||
// if(P.y > yTop) Code += 1; /* code for above */
|
||||
// else if(P.y < yBottom) Code += 2; /* code for below */
|
||||
//
|
||||
// if(P.x > xRight) Code += 4; /* code for right */
|
||||
// else if(P.x < xLeft) Code += 8; /* code for left */
|
||||
//
|
||||
// return Code;
|
||||
// }
|
||||
//
|
||||
// private static boolean rejectCheck(int outCode1, int outCode2)
|
||||
// {
|
||||
// if ((outCode1 & outCode2) != 0 ) return true;
|
||||
// return(false);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private static boolean acceptCheck(int outCode1, int outCode2)
|
||||
// {
|
||||
// if ( (outCode1 == 0) && (outCode2 == 0) ) return(true);
|
||||
// return(false);
|
||||
// }
|
||||
//
|
||||
// static boolean CohenSutherland2DClipper(Point P0,Point P1)
|
||||
// {
|
||||
// int outCode0,outCode1;
|
||||
// while(true)
|
||||
// {
|
||||
// outCode0 = outCodes(P0);
|
||||
// outCode1 = outCodes(P1);
|
||||
// if( rejectCheck(outCode0,outCode1) ) return(false);
|
||||
// if( acceptCheck(outCode0,outCode1) ) return(true);
|
||||
// if(outCode0 == 0)
|
||||
// {
|
||||
// double tempCoord; int tempCode;
|
||||
// tempCoord = P0.x; P0.x= P1.x; P1.x = tempCoord;
|
||||
// tempCoord = P0.y; P0.y= P1.y; P1.y = tempCoord;
|
||||
// tempCode = outCode0; outCode0 = outCode1; outCode1 = tempCode;
|
||||
// }
|
||||
// if( (outCode0 & 1) != 0 )
|
||||
// {
|
||||
// P0.x += (P1.x - P0.x)*(yTop - P0.y)/(P1.y - P0.y);
|
||||
// P0.y = yTop;
|
||||
// }
|
||||
// else
|
||||
// if( (outCode0 & 2) != 0 )
|
||||
// {
|
||||
// P0.x += (P1.x - P0.x)*(yBottom - P0.y)/(P1.y - P0.y);
|
||||
// P0.y = yBottom;
|
||||
// }
|
||||
// else
|
||||
// if( (outCode0 & 4) != 0 )
|
||||
// {
|
||||
// P0.y += (P1.y - P0.y)*(xRight - P0.x)/(P1.x - P0.x);
|
||||
// P0.x = xRight;
|
||||
// }
|
||||
// else
|
||||
// if( (outCode0 & 8) != 0 )
|
||||
// {
|
||||
// P0.y += (P1.y - P0.y)*(xLeft - P0.x)/(P1.x - P0.x);
|
||||
// P0.x = xLeft;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
158
src/org/oscim/view/utils/GlConfigChooser.java
Normal file
158
src/org/oscim/view/utils/GlConfigChooser.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package org.oscim.view.utils;
|
||||
|
||||
import javax.microedition.khronos.egl.EGL10;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.egl.EGLDisplay;
|
||||
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class GlConfigChooser implements GLSurfaceView.EGLConfigChooser {
|
||||
static private final String TAG = "ConfigChooser";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static int stencilSize = 0;
|
||||
|
||||
@Override
|
||||
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
|
||||
mValue = new int[1];
|
||||
|
||||
// Try to find a normal multisample configuration first.
|
||||
int[] configSpec = {
|
||||
EGL10.EGL_RED_SIZE, 5,
|
||||
EGL10.EGL_GREEN_SIZE, 6,
|
||||
EGL10.EGL_BLUE_SIZE, 5,
|
||||
EGL10.EGL_ALPHA_SIZE, 8,
|
||||
EGL10.EGL_DEPTH_SIZE, 16,
|
||||
// Requires that setEGLContextClientVersion(2) is called on the view.
|
||||
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
|
||||
EGL10.EGL_STENCIL_SIZE, 8,
|
||||
EGL10.EGL_NONE };
|
||||
|
||||
if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) {
|
||||
throw new IllegalArgumentException("eglChooseConfig failed");
|
||||
}
|
||||
int numConfigs = mValue[0];
|
||||
|
||||
if (numConfigs <= 0) {
|
||||
stencilSize = 4;
|
||||
|
||||
configSpec = new int[] {
|
||||
// EGL10.EGL_RENDERABLE_TYPE, 4, EGL10.EGL_NONE };
|
||||
EGL10.EGL_RED_SIZE, 8,
|
||||
EGL10.EGL_GREEN_SIZE, 8,
|
||||
EGL10.EGL_BLUE_SIZE, 8,
|
||||
EGL10.EGL_ALPHA_SIZE, 8,
|
||||
EGL10.EGL_DEPTH_SIZE, 16,
|
||||
EGL10.EGL_RENDERABLE_TYPE, 4 /* EGL_OPENGL_ES2_BIT */,
|
||||
EGL10.EGL_STENCIL_SIZE, 8,
|
||||
EGL10.EGL_NONE };
|
||||
|
||||
if (!egl.eglChooseConfig(display, configSpec, null, 0, mValue)) {
|
||||
throw new IllegalArgumentException("eglChooseConfig failed");
|
||||
}
|
||||
numConfigs = mValue[0];
|
||||
|
||||
if (numConfigs <= 0) {
|
||||
throw new IllegalArgumentException("No configs match configSpec");
|
||||
}
|
||||
} else {
|
||||
stencilSize = 8;
|
||||
}
|
||||
|
||||
// Get all matching configurations.
|
||||
EGLConfig[] configs = new EGLConfig[numConfigs];
|
||||
if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, mValue)) {
|
||||
throw new IllegalArgumentException("data eglChooseConfig failed");
|
||||
}
|
||||
|
||||
// CAUTION! eglChooseConfigs returns configs with higher bit depth
|
||||
// first: Even though we asked for rgb565 configurations, rgb888
|
||||
// configurations are considered to be "better" and returned first.
|
||||
// You need to explicitly filter the data returned by eglChooseConfig!
|
||||
|
||||
// for (int i = 0; i < configs.length; ++i) {
|
||||
// Log.i(TAG, printConfig(egl, display, configs[i]));
|
||||
// }
|
||||
|
||||
// int index = -1;
|
||||
// for (int i = 0; i < configs.length; ++i) {
|
||||
// // if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 8
|
||||
// // &&
|
||||
// // findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 0) {
|
||||
// // index = i;
|
||||
// // break;
|
||||
// // }
|
||||
// // else
|
||||
// if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == 8
|
||||
// &&
|
||||
// findConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE, 0) == 0
|
||||
// &&
|
||||
// findConfigAttrib(egl, display, configs[i], EGL10.EGL_DEPTH_SIZE, 0) == 24) {
|
||||
// index = i;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (index == -1) {
|
||||
// Log.w(TAG, "Did not find sane config, using first");
|
||||
// index = 0;
|
||||
// }
|
||||
int index = 0;
|
||||
|
||||
Log.i(TAG, "using: " + printConfig(egl, display, configs[index]));
|
||||
|
||||
EGLConfig config = configs.length > 0 ? configs[index] : null;
|
||||
if (config == null) {
|
||||
throw new IllegalArgumentException("No config chosen");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
// from quake2android
|
||||
private String printConfig(EGL10 egl, EGLDisplay display,
|
||||
EGLConfig config) {
|
||||
|
||||
int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
|
||||
int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
|
||||
int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
|
||||
int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
|
||||
int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
|
||||
int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
|
||||
|
||||
/*
|
||||
* EGL_CONFIG_CAVEAT value #define EGL_NONE 0x3038 #define EGL_SLOW_CONFIG 0x3050 #define
|
||||
* EGL_NON_CONFORMANT_CONFIG 0x3051
|
||||
*/
|
||||
|
||||
return String.format("EGLConfig rgba=%d%d%d%d depth=%d stencil=%d",
|
||||
Integer.valueOf(r), Integer.valueOf(g),
|
||||
Integer.valueOf(b), Integer.valueOf(a), Integer.valueOf(d),
|
||||
Integer.valueOf(s))
|
||||
+ " native="
|
||||
+ findConfigAttrib(egl, display, config, EGL10.EGL_NATIVE_RENDERABLE, 0)
|
||||
+ " buffer="
|
||||
+ findConfigAttrib(egl, display, config, EGL10.EGL_BUFFER_SIZE, 0)
|
||||
+ String.format(
|
||||
" caveat=0x%04x",
|
||||
Integer.valueOf(findConfigAttrib(egl, display, config,
|
||||
EGL10.EGL_CONFIG_CAVEAT, 0)));
|
||||
|
||||
}
|
||||
|
||||
private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
|
||||
int attribute, int defaultValue) {
|
||||
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
|
||||
return mValue[0];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private int[] mValue;
|
||||
|
||||
}
|
||||
145
src/org/oscim/view/utils/GlUtils.java
Normal file
145
src/org/oscim/view/utils/GlUtils.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.oscim.view.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Utility functions
|
||||
*/
|
||||
public class GlUtils {
|
||||
private static String TAG = "GlUtils";
|
||||
|
||||
/**
|
||||
* @param bitmap
|
||||
* ...
|
||||
* @return gl identifier
|
||||
*/
|
||||
public static int loadTextures(Bitmap bitmap) {
|
||||
|
||||
int[] textures = new int[1];
|
||||
GLES20.glGenTextures(1, textures, 0);
|
||||
|
||||
int textureID = textures[0];
|
||||
// Log.i(TAG, "new texture " + textureID + " " + textureCnt++);
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
|
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
|
||||
GLES20.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
|
||||
GLES20.GL_CLAMP_TO_EDGE);
|
||||
|
||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
|
||||
|
||||
return textureID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shaderType
|
||||
* shader type
|
||||
* @param source
|
||||
* shader code
|
||||
* @return gl identifier
|
||||
*/
|
||||
public static int loadShader(int shaderType, String source) {
|
||||
int shader = GLES20.glCreateShader(shaderType);
|
||||
if (shader != 0) {
|
||||
GLES20.glShaderSource(shader, source);
|
||||
GLES20.glCompileShader(shader);
|
||||
int[] compiled = new int[1];
|
||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
|
||||
if (compiled[0] == 0) {
|
||||
Log.e(TAG, "Could not compile shader " + shaderType + ":");
|
||||
Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
|
||||
GLES20.glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param vertexSource
|
||||
* ...
|
||||
* @param fragmentSource
|
||||
* ...
|
||||
* @return gl identifier
|
||||
*/
|
||||
public static int createProgram(String vertexSource, String fragmentSource) {
|
||||
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
|
||||
if (pixelShader == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int program = GLES20.glCreateProgram();
|
||||
if (program != 0) {
|
||||
checkGlError("glCreateProgram");
|
||||
GLES20.glAttachShader(program, vertexShader);
|
||||
checkGlError("glAttachShader");
|
||||
GLES20.glAttachShader(program, pixelShader);
|
||||
checkGlError("glAttachShader");
|
||||
GLES20.glLinkProgram(program);
|
||||
int[] linkStatus = new int[1];
|
||||
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
|
||||
if (linkStatus[0] != GLES20.GL_TRUE) {
|
||||
Log.e(TAG, "Could not link program: ");
|
||||
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
|
||||
GLES20.glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param op
|
||||
* ...
|
||||
*/
|
||||
public static void checkGlError(String op) {
|
||||
int error;
|
||||
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
|
||||
Log.e(TAG, op + ": glError " + error);
|
||||
// throw new RuntimeException(op + ": glError " + error);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkGlOutOfMemory(String op) {
|
||||
int error;
|
||||
boolean oom = false;
|
||||
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
|
||||
Log.e(TAG, op + ": glError " + error);
|
||||
// throw new RuntimeException(op + ": glError " + error);
|
||||
if (error == 1285)
|
||||
oom = true;
|
||||
}
|
||||
return oom;
|
||||
}
|
||||
}
|
||||
155
src/org/oscim/view/utils/PausableThread.java
Normal file
155
src/org/oscim/view/utils/PausableThread.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.oscim.view.utils;
|
||||
|
||||
/**
|
||||
* An abstract base class for threads which support pausing and resuming.
|
||||
*/
|
||||
public abstract class PausableThread extends Thread {
|
||||
private boolean mPausing;
|
||||
private boolean mShouldPause;
|
||||
|
||||
/**
|
||||
* Causes the current thread to wait until this thread is pausing.
|
||||
*/
|
||||
public final void awaitPausing() {
|
||||
synchronized (this) {
|
||||
while (!isInterrupted() && !isPausing()) {
|
||||
try {
|
||||
wait(100);
|
||||
} catch (InterruptedException e) {
|
||||
// restore the interrupted status
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
// first acquire the monitor which is used to call wait()
|
||||
synchronized (this) {
|
||||
super.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this thread is currently pausing, false otherwise.
|
||||
*/
|
||||
public final synchronized boolean isPausing() {
|
||||
return mPausing;
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread should stop its work temporarily.
|
||||
*/
|
||||
public final synchronized void pause() {
|
||||
if (!mShouldPause) {
|
||||
mShouldPause = true;
|
||||
takeabreak();
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The paused thread should continue with its work.
|
||||
*/
|
||||
public final synchronized void proceed() {
|
||||
if (mShouldPause) {
|
||||
mShouldPause = false;
|
||||
mPausing = false;
|
||||
afterPause();
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
setName(getThreadName());
|
||||
setPriority(getThreadPriority());
|
||||
|
||||
while (!isInterrupted()) {
|
||||
synchronized (this) {
|
||||
while (!isInterrupted() && (mShouldPause || !hasWork())) {
|
||||
try {
|
||||
if (mShouldPause) {
|
||||
mPausing = true;
|
||||
}
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
// restore the interrupted status
|
||||
interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isInterrupted()) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
doWork();
|
||||
} catch (InterruptedException e) {
|
||||
// restore the interrupted status
|
||||
interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
afterRun();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once when this thread continues to work after a pause. The default implementation is empty.
|
||||
*/
|
||||
protected void afterPause() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
protected void takeabreak() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once at the end of the {@link #run()} method. The default implementation is empty.
|
||||
*/
|
||||
protected void afterRun() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this thread is not paused and should do its work.
|
||||
*
|
||||
* @throws InterruptedException
|
||||
* if the thread has been interrupted.
|
||||
*/
|
||||
protected abstract void doWork() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* @return the name of this thread.
|
||||
*/
|
||||
protected abstract String getThreadName();
|
||||
|
||||
/**
|
||||
* @return the priority of this thread. The default value is {@link Thread#NORM_PRIORITY}.
|
||||
*/
|
||||
protected int getThreadPriority() {
|
||||
return Thread.NORM_PRIORITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this thread has some work to do, false otherwise.
|
||||
*/
|
||||
protected abstract boolean hasWork();
|
||||
}
|
||||
Reference in New Issue
Block a user