implement rotating map

git-svn-id: https://osmand.googlecode.com/svn/trunk@78 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-05-23 20:59:55 +00:00
parent 891a38e697
commit 56befb372c
11 changed files with 199 additions and 74 deletions

View file

@ -19,36 +19,51 @@ public class ToDoConstants {
*/
public int DESCRIBE_ABOUT_AUTHORS = 8;
// 0. Minimize memory used for index & improve time for read index
//// TODO for releasing version
// 1. POI SEARCH NEAR TO YOU
// 2. FIX BACK TO your location & gps & point of view (may be compass)
// 3. Revise UI icons/layout
// 5. Enable city/streets/buildings index
// 7. Search for city/streets/buildings!
// 8. Enable change POI directly on map
// 9. Log to see when exception occurred (android)
// 10. Specify auto-rotating map (compass).
// TODO ANDROID
// 0. Minimize memory used for index & improve time for reading index
// 1. POI search near to map location (show categories & type). First cut. (implement incremental search)
// 3. Revise osmand UI. Preparing new icons.
// 2. Showing compass on the map : use device compass if exists(?)
// 5. Search for city/streets/buildings
// 9. Config file log & see log from file (when exception happened to see from device)
// 11. Print out additional info speed, altitude, number of satellites
// 12. Show point where are you going (the arrow not the point)
// 13. Save point as favorite
// 14. Show zoom level directly on map
// 15. Investigate interruption of progress (is it available & how to support it)
// -------------------
// 8. Enable change POI directly on map (requires OSM login)
// 13. Save point as favourite & introduce favourite points dialog
// 14. Show zoom level on map
// 15. Investigate interruption of any long running operation & implement where it is needed
// 16. Support open street bugs api.
// 17. Enable go to location specifying coordinates
// 18. Implement go to point
// 19. Show how map is rotated where north/south on map (do not consider compass)
// 20. Implement save track/route to gpx (?)
// BUGS Androd :
// FIXME Bugs Androd :
// 0. FIX TODO for partial loading rotated map
// 1. When firstly run osmand navigation (from notification bar) show map & go to menu shows desktop.
// No chance to close application
// 3. Fix progress information (loading indices) for android version
// 4. Fix when POI selected & enable button backToLocation
// TODO SWING:
// 1. download tiles without using dir tiles
// 2. Config file log & see log from file
// 3. Reinvent index mechanism (save in zip file with tile indexes, save city/town addresses separately, read partially !)
// 4. Invent different file extensions for poi.index, address.index,...
/// SWING version :
// TODO :
// 1. Accept amenity as way
// 1. Fix TODO in files
// Max letter :
// 1. Fix bug 1
// 2. Create for each screen activity
// 3. Implement incremental search (reduce first time display to 10 & depth 2)
// 4. Improve navigate back/forward between screens
// 5. Implement exit confirmation
// DONE ANDROID :
// 12. Show information of where are you going (the arrow on the map)
// 10. Specify auto-rotating map (bearing of your direction)
// DONE SWING
// 3. download tiles without using dir tiles
// 4. Config file log & see log from file
// 5. Reinvent index mechanism (save in zip file with tile indexes, save city/town addresses separately, read partially !)
// 6. Invent different file extensions for poi.index, address.index,...
}

View file

@ -88,7 +88,7 @@ public class DataTileManager<T> {
if(isEmpty()){
return Collections.emptyList();
}
int dp = 1;
int dp = 0;
List<T> l = null;
while (l == null || l.isEmpty()) {
l = getClosestObjects(latitude, longitude, dp, dp + defaultStep);
@ -106,8 +106,10 @@ public class DataTileManager<T> {
int tileY = (int) MapUtils.getTileNumberY(zoom, latitude);
List<T> result = new ArrayList<T>();
if(startDepth <= 0){
putObjects(tileX, tileY, result);
startDepth = 1;
}
// that's very difficult way visiting node :
// similar to visit by spiral

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal">
<ImageView android:id="@+id/icon" android:layout_width="20px"
<ImageView android:id="@+id/icon" android:layout_width="25px"
android:paddingLeft="2px" android:paddingRight="2px"
android:paddingTop="2px" android:layout_height="fill_parent"/>
<TextView android:id="@+id/label" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="20px" />
android:layout_height="wrap_content" android:textSize="25px" />
</LinearLayout>

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="rotate_map_to_bearing_descr">Rotate map to bearing of your direction</string>
<string name="rotate_map_to_bearing">Rotate map</string>
<string name="show_poi_over_map_description">Show POI on map</string>
<string name="show_poi_over_map">Show POI</string>
<string name="map_tile_source_descr">Choose the source of tiles:</string>

View file

@ -5,6 +5,7 @@
<CheckBoxPreference android:key="use_internet_to_download_tiles" android:title="@string/use_internet" android:summary="@string/use_internet_to_download_tile"></CheckBoxPreference>
<CheckBoxPreference android:key="show_poi_over_map" android:title="@string/show_poi_over_map" android:summary="@string/show_poi_over_map_description"></CheckBoxPreference>
<CheckBoxPreference android:key="rotate_map_to_bearing" android:title="@string/rotate_map_to_bearing" android:summary="@string/rotate_map_to_bearing_descr"></CheckBoxPreference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/map_source"><ListPreference android:title="@string/map_tile_source" android:summary="@string/map_tile_source_descr" android:key="map_tile_sources"></ListPreference>
</PreferenceCategory>

View file

@ -30,6 +30,13 @@ public class OsmandSettings {
return prefs.getBoolean(SHOW_POI_OVER_MAP, false);
}
// this value string is synchronized with android.xml preference name
public static final String ROTATE_MAP_TO_BEARING = "rotate_map_to_bearing";
public static boolean isRotateMapToBearing(Context ctx){
SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
return prefs.getBoolean(ROTATE_MAP_TO_BEARING, false);
}
// this value string is synchronized with android.xml preference name
public static final String MAP_TILE_SOURCES = "map_tile_sources";

View file

@ -135,16 +135,22 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
}
public void setLocation(Location location){
locationLayer.setLastKnownLocation(location);
// Do very strange manipulation to call redraw only once
locationLayer.setLastKnownLocation(location, true);
if (location != null) {
if (linkLocationWithMap) {
if (location.hasBearing() && OsmandSettings.isRotateMapToBearing(this)) {
mapView.setRotateWithLocation(-location.getBearing(), location.getLatitude(), location.getLongitude());
} else {
mapView.setLatLon(location.getLatitude(), location.getLongitude());
}
} else {
mapView.prepareImage();
}
} else {
if (!linkLocationWithMap) {
backToLocation.setVisibility(View.VISIBLE);
}
}
}
@ -191,6 +197,9 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
if(mapView.getMap() != OsmandSettings.getMapTileSource(this)){
mapView.setMap(OsmandSettings.getMapTileSource(this));
}
if(!OsmandSettings.isRotateMapToBearing(this)){
mapView.setRotate(0);
}
if(mapView.getLayers().contains(poiMapLayer) != OsmandSettings.isShowingPoiOverMap(this)){
if(OsmandSettings.isShowingPoiOverMap(this)){
mapView.addLayer(poiMapLayer);

View file

@ -113,6 +113,12 @@ public class SearchActivity extends ListActivity {
super(SearchActivity.this, R.layout.searchlist, (List<?>) list);
}
@Override
public int getCount() {
int c = super.getCount();
return c > 20 ? 20 : c;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = getLayoutInflater();
View row = inflater.inflate(R.layout.searchlist, parent, false);

View file

@ -23,6 +23,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
private CheckBoxPreference showPoiOnMap;
private CheckBoxPreference useInternetToDownloadTiles;
private ListPreference tileSourcePreference;
private CheckBoxPreference rotateMapToBearing;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -33,6 +34,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
useInternetToDownloadTiles.setOnPreferenceChangeListener(this);
showPoiOnMap =(CheckBoxPreference) screen.findPreference(OsmandSettings.SHOW_POI_OVER_MAP);
showPoiOnMap.setOnPreferenceChangeListener(this);
rotateMapToBearing =(CheckBoxPreference) screen.findPreference(OsmandSettings.ROTATE_MAP_TO_BEARING);
rotateMapToBearing.setOnPreferenceChangeListener(this);
tileSourcePreference =(ListPreference) screen.findPreference(OsmandSettings.MAP_TILE_SOURCES);
tileSourcePreference.setOnPreferenceChangeListener(this);
@ -45,6 +48,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
super.onResume();
useInternetToDownloadTiles.setChecked(OsmandSettings.isUsingInternetToDownloadTiles(this));
showPoiOnMap.setChecked(OsmandSettings.isShowingPoiOverMap(this));
rotateMapToBearing.setChecked(OsmandSettings.isRotateMapToBearing(this));
List<TileSourceTemplate> list = TileSourceManager.getKnownSourceTemplates();
String[] entries = new String[list.size()];
@ -70,6 +74,9 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
} else if(preference == useInternetToDownloadTiles){
edit.putBoolean(OsmandSettings.USE_INTERNET_TO_DOWNLOAD_TILES, (Boolean) newValue);
edit.commit();
} else if(preference == rotateMapToBearing){
edit.putBoolean(OsmandSettings.ROTATE_MAP_TO_BEARING, (Boolean) newValue);
edit.commit();
} else if (preference == tileSourcePreference) {
edit.putString(OsmandSettings.MAP_TILE_SOURCES, (String) newValue);
edit.commit();

View file

@ -13,8 +13,10 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@ -48,6 +50,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
private double latitude = 0d;
private float rotate = 0;
// name of source map
private ITileSource map = null;
@ -65,6 +69,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
Paint paintGrayFill;
Paint paintWhiteFill;
Paint paintBlack;
Paint paintBitmap;
@ -84,15 +89,22 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
paintGrayFill = new Paint();
paintGrayFill.setColor(Color.GRAY);
paintGrayFill.setStyle(Style.FILL);
// when map rotate
paintGrayFill.setAntiAlias(true);
paintWhiteFill = new Paint();
paintWhiteFill.setColor(Color.WHITE);
paintWhiteFill.setStyle(Style.FILL);
// when map rotate
paintWhiteFill.setAntiAlias(true);
paintBlack = new Paint();
paintBlack.setStyle(Style.STROKE);
paintBlack.setColor(Color.BLACK);
paintBitmap = new Paint();
paintBitmap.setFilterBitmap(true);
setClickable(true);
getHolder().addCallback(this);
@ -140,12 +152,12 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
}
public double getXTile(){
return MapUtils.getTileNumberX(zoom, longitude);
public float getXTile(){
return (float) MapUtils.getTileNumberX(zoom, longitude);
}
public double getYTile(){
return MapUtils.getTileNumberY(zoom, latitude);
public float getYTile(){
return (float) MapUtils.getTileNumberY(zoom, latitude);
}
@ -157,6 +169,27 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
}
}
public void setRotate(float rotate) {
float dif = this.rotate - rotate;
if (dif > 2 || dif < -2) {
this.rotate = rotate;
animatedDraggingThread.stopDragging();
prepareImage();
}
}
public void setRotateWithLocation(float rotate, double latitude, double longitude){
animatedDraggingThread.stopDragging();
this.rotate = rotate;
this.latitude = latitude;
this.longitude = longitude;
prepareImage();
}
public float getRotate() {
return rotate;
}
public ITileSource getMap() {
return map;
}
@ -230,33 +263,59 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
}
}
protected void calculateTileRectangle(RectF pixRect, float cx, float cy, RectF tileRect){
float x1 = calcDiffTileX(pixRect.left - cx, pixRect.top - cy);
float x2 = calcDiffTileX(pixRect.left - cx, pixRect.bottom - cy);
float x3 = calcDiffTileX(pixRect.right - cx, pixRect.top - cy);
float x4 = calcDiffTileX(pixRect.right - cx, pixRect.bottom - cy);
float y1 = calcDiffTileY(pixRect.left - cx, pixRect.top - cy);
float y2 = calcDiffTileY(pixRect.left - cx, pixRect.bottom - cy);
float y3 = calcDiffTileY(pixRect.right - cx, pixRect.top - cy);
float y4 = calcDiffTileY(pixRect.right - cx, pixRect.bottom - cy);
float l = Math.min(Math.min(x1, x2), Math.min(x3, x4)) + getXTile();
float r = Math.max(Math.max(x1, x2), Math.max(x3, x4)) + getXTile();
float t = Math.min(Math.min(y1, y2), Math.min(y3, y4)) + getYTile();
float b = Math.max(Math.max(y1, y2), Math.max(y3, y4)) + getYTile();
tileRect.set(l, t, r, b);
}
// used only to save space & reuse
protected RectF tilesRect = new RectF();
protected RectF boundsRect = new RectF();
public void prepareImage() {
if (OsmandSettings.isUsingInternetToDownloadTiles(getContext())) {
MapTileDownloader.getInstance().refuseAllPreviousRequests();
}
int width = getWidth();
int height = getHeight();
int tileSize = getTileSize();
int xTileLeft = (int) Math.floor(getXTile() - width / (2d * getTileSize()));
int yTileUp = (int) Math.floor(getYTile() - height / (2d * getTileSize()));
int startingX = (int) ((xTileLeft - getXTile()) * getTileSize() + getWidth() / 2);
int startingY = (int) ((yTileUp - getYTile()) * getTileSize() + getHeight() / 2);
float tileX = getXTile();
float tileY = getYTile();
SurfaceHolder holder = getHolder();
synchronized (holder) {
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
// canvas.rotate(45);
try {
for (int i = 0; i * tileSize + startingX < width; i++) {
for (int j = 0; j * tileSize + startingY < height; j++) {
ResourceManager mgr = ResourceManager.getResourceManager();
Bitmap bmp = mgr.getTileImageForMapAsync(map, xTileLeft + i, yTileUp + j, zoom, OsmandSettings.isUsingInternetToDownloadTiles(getContext()));
boolean useInternet = OsmandSettings.isUsingInternetToDownloadTiles(getContext());
float w = getWidth() / 2;
float h = getHeight() / 2;
canvas.rotate(rotate, w , h);
boundsRect.set(0, 0, getWidth(), getHeight());
calculateTileRectangle(boundsRect, w, h, tilesRect);
try {
int left = (int) FloatMath.floor(tilesRect.left);
int top = (int) FloatMath.floor(tilesRect.top);
int width = (int) (FloatMath.ceil(tilesRect.right) - left);
int height = (int) (FloatMath.ceil(tilesRect.bottom) - top);
for (int i = 0; i <width; i++) {
for (int j = 0; j< height; j++) {
float x1 = (i + left - tileX) * tileSize + w;
float y1 = (j + top - tileY) * tileSize + h;
Bitmap bmp = mgr.getTileImageForMapAsync(map, left + i, top + j, zoom, useInternet);
if (bmp == null) {
drawEmptyTile(canvas, i * tileSize + startingX, j * tileSize + startingY);
drawEmptyTile(canvas, (int) x1, (int) y1);
} else {
canvas.drawBitmap(bmp, i * tileSize + startingX, j * tileSize + startingY, null);
canvas.drawBitmap(bmp, x1, y1, paintBitmap);
}
}
}
@ -270,7 +329,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
public void tileDownloaded(DownloadRequest request) {
if(request == null){
// TODO estimate bounds for rotated map
if(request == null || rotate != 0){
// we don't know exact images were changed
prepareImage();
return;
@ -285,10 +345,9 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
(i + getTileSize() >= 0 && i < getWidth()) && (j + getTileSize() >= 0 && j < getHeight())) {
SurfaceHolder holder = getHolder();
synchronized (holder) {
// TODO
Canvas canvas = holder.lockCanvas(new Rect(i, j, getTileSize() + i, getTileSize() + j));
if (canvas != null) {
// canvas.rotate(45);
canvas.rotate(rotate,getWidth()/2, getHeight()/2);
try {
ResourceManager mgr = ResourceManager.getResourceManager();
Bitmap bmp = mgr.getTileImageForMapSync(map, request.xTile, request.yTile, zoom, false);
@ -309,14 +368,26 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
/////////////////////////////////// DRAGGING PART ///////////////////////////////////////
public float calcDiffTileY(float dx, float dy){
float rad = (float) Math.toRadians(rotate);
return (-FloatMath.sin(rad) * dx + FloatMath.cos(rad) * dy) / getTileSize();
}
public float calcDiffTileX(float dx, float dy){
float rad = (float) Math.toRadians(rotate);
return (FloatMath.cos(rad) * dx + FloatMath.sin(rad) * dy) / getTileSize();
}
@Override
public void dragTo(float fromX, float fromY, float toX, float toY){
float dx = (fromX - toX)/getTileSize();
float dy = (fromY - toY)/getTileSize();
this.latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + dy);
this.longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + dx);
float dx = (fromX - toX) ;
float dy = (fromY - toY);
float fy = calcDiffTileY(dx, dy);
float fx = calcDiffTileX(dx, dy);
this.latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + fy);
this.longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + fx);
prepareImage();
if(locationListener != null){
locationListener.locationChanged(latitude, longitude, this);

View file

@ -7,6 +7,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Paint.Style;
import android.location.Location;
import android.util.FloatMath;
import android.view.MotionEvent;
import com.osmand.osm.MapUtils;
@ -52,6 +53,7 @@ public class PointLocationLayer implements OsmandMapLayer {
}
// TODO simplify calculation if possible
@Override
public void onDraw(Canvas canvas) {
if (isLocationVisible(lastKnownLocation)) {
@ -62,9 +64,7 @@ public class PointLocationLayer implements OsmandMapLayer {
int radius = MapUtils.getLengthXFromMeters(view.getZoom(), view.getLatitude(), view.getLongitude(), lastKnownLocation
.getAccuracy(), view.getTileSize(), view.getWidth());
if (locationX >= 0 && locationY >= 0) {
canvas.drawCircle(locationX, locationY, RADIUS, location);
}
if (radius > RADIUS) {
canvas.drawCircle(locationX, locationY, radius, area);
}
@ -103,12 +103,15 @@ public class PointLocationLayer implements OsmandMapLayer {
if(l == null || view == null){
return false;
}
int newX = MapUtils.getPixelShiftX(view.getZoom(),
l.getLongitude(), view.getLongitude(), view.getTileSize()) +
view.getWidth()/2;
int newY = MapUtils.getPixelShiftY(view.getZoom(),
l.getLatitude(), view.getLatitude() , view.getTileSize()) +
view.getHeight()/2;
int cx = view.getWidth()/2;
int cy = view.getHeight()/2;
int dx = MapUtils.getPixelShiftX(view.getZoom(),
l.getLongitude(), view.getLongitude(), view.getTileSize());
int dy = MapUtils.getPixelShiftY(view.getZoom(),
l.getLatitude(), view.getLatitude() , view.getTileSize());
float rad = (float) Math.toRadians(view.getRotate());
int newX = (int) (dx * FloatMath.cos(rad) - dy * FloatMath.sin(rad) + cx);
int newY = (int) (dx * FloatMath.sin(rad) + dy * FloatMath.cos(rad) + cy);
int radius = MapUtils.getLengthXFromMeters(view.getZoom(), view.getLatitude(), view.getLongitude(),
l.getAccuracy(), view.getTileSize(), view.getWidth());
if(newX >= 0 && newX <= view.getWidth() && newY >=0 && newY <= view.getHeight()){
@ -127,13 +130,15 @@ public class PointLocationLayer implements OsmandMapLayer {
return lastKnownLocation;
}
public void setLastKnownLocation(Location lastKnownLocation) {
boolean redraw = isLocationVisible(this.lastKnownLocation) || isLocationVisible(lastKnownLocation);
public void setLastKnownLocation(Location lastKnownLocation, boolean doNotRedraw) {
this.lastKnownLocation = lastKnownLocation;
if (!doNotRedraw) {
boolean redraw = isLocationVisible(this.lastKnownLocation) || isLocationVisible(lastKnownLocation);
if (redraw) {
view.prepareImage();
}
}
}
@Override
public void destroyLayer() {