implement osm bugs layer

git-svn-id: https://osmand.googlecode.com/svn/trunk@139 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-06-05 10:42:41 +00:00
parent c0efd98ff6
commit c6e16ea747
11 changed files with 334 additions and 12 deletions

View file

@ -11,7 +11,7 @@ public class ToDoConstants {
*/ */
public int DESCRIBE_ABOUT_AUTHORS = 8; public int DESCRIBE_ABOUT_AUTHORS = 8;
// TODO ANDROID NOT in release 0.1 // TODO ANDROID
// 8. Enable change POI directly on map (requires OSM login) // 8. Enable change POI directly on map (requires OSM login)
// 16. Support open street bugs api. // 16. Support open street bugs api.
// 20. Implement save track/route to gpx (?) // 20. Implement save track/route to gpx (?)
@ -22,14 +22,13 @@ public class ToDoConstants {
// 31. Translation. // 31. Translation.
// 32. Introduce POI predefined filters (car filter(other-fuel, transportation-car_wash, show-car) and others) // 32. Introduce POI predefined filters (car filter(other-fuel, transportation-car_wash, show-car) and others)
// 33. Build transport locations (investigate) // 33. Build transport locations (investigate)
// 34. Investigate routing (bicycle, car) // 34. Investigate routing (bicycle, car)
// 35. Enable trackball navigation in android
// FIXME Bugs Android : // TODO swing
// 10. Notification is gone after clear all notifications (TODO test)
// TODO swing NOT in release 0.1
// 1. Download tiles without using dir tiles // 1. Download tiles without using dir tiles
// 2. Internal (Simplify MapPanel - introduce layers for it)
// DONE ANDROID : // DONE ANDROID :

View file

@ -24,7 +24,7 @@ import com.osmand.LogUtil;
public class MapTileDownloader { public class MapTileDownloader {
// Application constants // Application constants
public static String APP_NAME = "OsmAnd"; public static String APP_NAME = "OsmAnd";
public static String APP_VERSION = "0.1"; public static String APP_VERSION = "0.2";
// Download manager tile settings // Download manager tile settings
@ -131,7 +131,15 @@ public class MapTileDownloader {
public void refuseAllPreviousRequests(){ public void refuseAllPreviousRequests(){
threadPoolExecutor.getQueue().clear(); //FIXME it could cause NPE in android implementation think abou different style
// That's very strange because exception in impl of queue (possibly wrong impl)
// threadPoolExecutor.getQueue().clear();
while(!threadPoolExecutor.getQueue().isEmpty()){
try {
threadPoolExecutor.getQueue().take();
} catch (InterruptedException e) {
}
}
} }
public void requestToDownload(DownloadRequest request){ public void requestToDownload(DownloadRequest request){

View file

@ -104,7 +104,9 @@ public class OsmExtractionUI implements IMapLocationListener {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){ Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){
@Override @Override
public void uncaughtException(Thread t, Throwable e) { public void uncaughtException(Thread t, Throwable e) {
log.error("Error in thread " + t.getName(), e); if(!(e instanceof ThreadDeath)){
log.error("Error in thread " + t.getName(), e);
}
defaultHandler.uncaughtException(t, e); defaultHandler.uncaughtException(t, e);
} }
}); });

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="show_osm_bugs_descr">Show open street bugs on map</string>
<string name="show_osm_bugs">Show open street bugs</string>
<string name="favourites_activity">List of favourite points</string> <string name="favourites_activity">List of favourite points</string>
<string name="add_to_favourite">Add to Favourites</string> <string name="add_to_favourite">Add to Favourites</string>
<string name="use_english_names_descr">Select use native or english names</string> <string name="use_english_names_descr">Select use native or english names</string>
@ -21,7 +23,7 @@
<string name="map_view_3d">Map View 3D</string> <string name="map_view_3d">Map View 3D</string>
<string name="rotate_map_to_bearing_descr">Rotate map to bearing of your direction</string> <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="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_description">Show POI on map (zoom &gt; 15)</string>
<string name="show_poi_over_map">Show POI</string> <string name="show_poi_over_map">Show POI</string>
<string name="map_tile_source_descr">Choose the source of tiles:</string> <string name="map_tile_source_descr">Choose the source of tiles:</string>
<string name="map_tile_source">Map tile source</string> <string name="map_tile_source">Map tile source</string>

View file

@ -9,7 +9,7 @@
<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="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> <CheckBoxPreference android:key="rotate_map_to_bearing" android:title="@string/rotate_map_to_bearing" android:summary="@string/rotate_map_to_bearing_descr"></CheckBoxPreference>
<CheckBoxPreference android:key="show_view_angle" android:title="@string/show_view_angle" android:summary="@string/show_view_angle_descr"></CheckBoxPreference> <CheckBoxPreference android:key="show_view_angle" android:title="@string/show_view_angle" android:summary="@string/show_view_angle_descr"></CheckBoxPreference>
<CheckBoxPreference android:key="show_osm_bugs" android:title="@string/show_osm_bugs" android:summary="@string/show_osm_bugs_descr"></CheckBoxPreference>
<ListPreference android:key="position_on_map" android:title="@string/position_on_map" android:summary="@string/position_on_map_descr"></ListPreference> <ListPreference android:key="position_on_map" android:title="@string/position_on_map" android:summary="@string/position_on_map_descr"></ListPreference>
</PreferenceCategory> </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 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>

View file

@ -33,6 +33,14 @@ public class OsmandSettings {
return prefs.getBoolean(SHOW_POI_OVER_MAP, false); return prefs.getBoolean(SHOW_POI_OVER_MAP, false);
} }
// this value string is synchronized with settings_pref.xml preference name
public static final String SHOW_OSM_BUGS = "show_osm_bugs";
public static boolean isShowingOsmBugs(Context ctx){
SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
return prefs.getBoolean(SHOW_OSM_BUGS, false);
}
// this value string is synchronized with settings_pref.xml preference name // this value string is synchronized with settings_pref.xml preference name
public static final String SHOW_VIEW_ANGLE = "show_view_angle"; public static final String SHOW_VIEW_ANGLE = "show_view_angle";
public static boolean isShowingViewAngle(Context ctx){ public static boolean isShowingViewAngle(Context ctx){

View file

@ -43,6 +43,7 @@ import com.osmand.data.preparation.MapTileDownloader;
import com.osmand.map.IMapLocationListener; import com.osmand.map.IMapLocationListener;
import com.osmand.osm.LatLon; import com.osmand.osm.LatLon;
import com.osmand.views.MapInfoLayer; import com.osmand.views.MapInfoLayer;
import com.osmand.views.OsmBugsLayer;
import com.osmand.views.OsmandMapTileView; import com.osmand.views.OsmandMapTileView;
import com.osmand.views.POIMapLayer; import com.osmand.views.POIMapLayer;
import com.osmand.views.PointLocationLayer; import com.osmand.views.PointLocationLayer;
@ -61,11 +62,14 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
private PointNavigationLayer navigationLayer; private PointNavigationLayer navigationLayer;
private POIMapLayer poiMapLayer; private POIMapLayer poiMapLayer;
private MapInfoLayer mapInfoLayer; private MapInfoLayer mapInfoLayer;
private OsmBugsLayer osmBugsLayer;
private WakeLock wakeLock; private WakeLock wakeLock;
private boolean sensorRegistered = false; private boolean sensorRegistered = false;
private Handler sensorHandler = new Handler(); private Handler sensorHandler = new Handler();
private final static String BACK_TO_LOCATION = "BACK_TO_LOCATION"; private final static String BACK_TO_LOCATION = "BACK_TO_LOCATION";
private final static String POINT_NAVIGATE_LAT = "POINT_NAVIGATE_LAT"; private final static String POINT_NAVIGATE_LAT = "POINT_NAVIGATE_LAT";
private final static String POINT_NAVIGATE_LON = "POINT_NAVIGATE_LON"; private final static String POINT_NAVIGATE_LON = "POINT_NAVIGATE_LON";
@ -95,6 +99,7 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
mapView.addLayer(locationLayer); mapView.addLayer(locationLayer);
mapInfoLayer = new MapInfoLayer(this); mapInfoLayer = new MapInfoLayer(this);
mapView.addLayer(mapInfoLayer); mapView.addLayer(mapInfoLayer);
osmBugsLayer = new OsmBugsLayer();
@ -305,6 +310,14 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
mapView.removeLayer(poiMapLayer); mapView.removeLayer(poiMapLayer);
} }
} }
if(mapView.getLayers().contains(osmBugsLayer) != OsmandSettings.isShowingOsmBugs(this)){
if(OsmandSettings.isShowingOsmBugs(this)){
mapView.addLayer(osmBugsLayer);
} else {
mapView.removeLayer(osmBugsLayer);
}
}
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE); LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
service.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 0, this); service.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 0, this);

View file

@ -27,6 +27,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
private CheckBoxPreference showViewAngle; private CheckBoxPreference showViewAngle;
private ListPreference positionOnMap; private ListPreference positionOnMap;
private CheckBoxPreference useEnglishNames; private CheckBoxPreference useEnglishNames;
private CheckBoxPreference showOsmBugs;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -41,6 +42,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
rotateMapToBearing.setOnPreferenceChangeListener(this); rotateMapToBearing.setOnPreferenceChangeListener(this);
showViewAngle =(CheckBoxPreference) screen.findPreference(OsmandSettings.SHOW_VIEW_ANGLE); showViewAngle =(CheckBoxPreference) screen.findPreference(OsmandSettings.SHOW_VIEW_ANGLE);
showViewAngle.setOnPreferenceChangeListener(this); showViewAngle.setOnPreferenceChangeListener(this);
showOsmBugs =(CheckBoxPreference) screen.findPreference(OsmandSettings.SHOW_OSM_BUGS);
showOsmBugs.setOnPreferenceChangeListener(this);
useEnglishNames =(CheckBoxPreference) screen.findPreference(OsmandSettings.USE_ENGLISH_NAMES); useEnglishNames =(CheckBoxPreference) screen.findPreference(OsmandSettings.USE_ENGLISH_NAMES);
useEnglishNames.setOnPreferenceChangeListener(this); useEnglishNames.setOnPreferenceChangeListener(this);
@ -59,6 +62,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
showPoiOnMap.setChecked(OsmandSettings.isShowingPoiOverMap(this)); showPoiOnMap.setChecked(OsmandSettings.isShowingPoiOverMap(this));
rotateMapToBearing.setChecked(OsmandSettings.isRotateMapToBearing(this)); rotateMapToBearing.setChecked(OsmandSettings.isRotateMapToBearing(this));
showViewAngle.setChecked(OsmandSettings.isShowingViewAngle(this)); showViewAngle.setChecked(OsmandSettings.isShowingViewAngle(this));
showOsmBugs.setChecked(OsmandSettings.isShowingOsmBugs(this));
useEnglishNames.setChecked(OsmandSettings.usingEnglishNames(this)); useEnglishNames.setChecked(OsmandSettings.usingEnglishNames(this));
String[] e = new String[] { "Center", "Bottom" }; String[] e = new String[] { "Center", "Bottom" };
positionOnMap.setEntryValues(e); positionOnMap.setEntryValues(e);
@ -99,6 +103,9 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
} else if(preference == showViewAngle){ } else if(preference == showViewAngle){
edit.putBoolean(OsmandSettings.SHOW_VIEW_ANGLE, (Boolean) newValue); edit.putBoolean(OsmandSettings.SHOW_VIEW_ANGLE, (Boolean) newValue);
edit.commit(); edit.commit();
} else if(preference == showOsmBugs){
edit.putBoolean(OsmandSettings.SHOW_OSM_BUGS, (Boolean) newValue);
edit.commit();
} else if(preference == positionOnMap){ } else if(preference == positionOnMap){
edit.putInt(OsmandSettings.POSITION_ON_MAP, positionOnMap.findIndexOfValue((String) newValue)); edit.putInt(OsmandSettings.POSITION_ON_MAP, positionOnMap.findIndexOfValue((String) newValue));
edit.commit(); edit.commit();

View file

@ -0,0 +1,278 @@
package com.osmand.views;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.Toast;
import com.osmand.LogUtil;
import com.osmand.osm.MapUtils;
public class OsmBugsLayer implements OsmandMapLayer {
private static final Log log = LogUtil.getLog(OsmBugsLayer.class);
private final static int startZoom = 8;
private final int SEARCH_LIMIT = 100;
private OsmandMapTileView view;
private Handler handlerToLoop;
private Rect pixRect = new Rect();
private RectF tileRect = new RectF();
private List<OpenStreetBug> objects = new ArrayList<OpenStreetBug>();
private Paint pointClosedUI;
private Paint pointOpenedUI;
private Pattern patternToParse = Pattern.compile("putAJAXMarker\\((\\d*), ((\\d|\\.)*), ((\\d|\\.)*), '([^']*)', (\\d)\\);");
private double cTopLatitude;
private double cBottomLatitude;
private double cLeftLongitude;
private double cRightLongitude;
private int czoom;
@Override
public void initLayer(OsmandMapTileView view) {
this.view = view;
synchronized (this) {
if (handlerToLoop == null) {
new Thread("Open street bugs layer") {
@Override
public void run() {
Looper.prepare();
handlerToLoop = new Handler();
Looper.loop();
}
}.start();
}
}
pointOpenedUI = new Paint();
pointOpenedUI.setColor(Color.RED);
pointOpenedUI.setAlpha(200);
pointOpenedUI.setAntiAlias(true);
pointClosedUI = new Paint();
pointClosedUI.setColor(Color.GREEN);
pointClosedUI.setAlpha(200);
pointClosedUI.setAntiAlias(true);
pixRect.set(0, 0, view.getWidth(), view.getHeight());
}
@Override
public void destroyLayer() {
synchronized (this) {
if(handlerToLoop != null){
handlerToLoop.post(new Runnable(){
@Override
public void run() {
Looper.myLooper().quit();
}
});
handlerToLoop = null;
}
}
}
@Override
public boolean drawInScreenPixels() {
return false;
}
@Override
public void onDraw(Canvas canvas) {
if (view.getZoom() >= startZoom) {
pixRect.set(0, 0, view.getWidth(), view.getHeight());
view.calculateTileRectangle(pixRect, view.getCenterPointX(),
view.getCenterPointY(), view.getXTile(), view.getYTile(), tileRect);
double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top);
double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left);
double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom);
double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right);
// request to load
requestToLoad(topLatitude, leftLongitude, bottomLatitude, rightLongitude, view.getZoom());
for (OpenStreetBug o : objects) {
int x = view.getMapXForPoint(o.getLongitude());
int y = view.getMapYForPoint(o.getLatitude());
canvas.drawCircle(x, y, getRadiusBug(view.getZoom()), o.isOpened()? pointOpenedUI: pointClosedUI);
}
}
}
public int getRadiusBug(int zoom){
if(zoom < startZoom){
return 0;
} else if(zoom <= 12){
return 6;
} else if(zoom <= 15){
return 10;
} else if(zoom == 16){
return 13;
} else if(zoom == 17){
return 15;
} else {
return 18;
}
}
public void requestToLoad(double topLatitude, double leftLongitude, double bottomLatitude,double rightLongitude, final int zoom){
boolean inside = cTopLatitude >= topLatitude && cLeftLongitude <= leftLongitude && cRightLongitude >= rightLongitude
&& cBottomLatitude <= bottomLatitude;
if(!inside || (czoom != zoom && objects.size() >= SEARCH_LIMIT)){
handlerToLoop.removeMessages(1);
final double nTopLatitude = topLatitude + (topLatitude -bottomLatitude);
final double nBottomLatitude = bottomLatitude - (topLatitude -bottomLatitude);
final double nLeftLongitude = leftLongitude - (rightLongitude - leftLongitude);
final double nRightLongitude = rightLongitude + (rightLongitude - leftLongitude);
Message msg = Message.obtain(handlerToLoop, new Runnable() {
@Override
public void run() {
if(handlerToLoop != null && !handlerToLoop.hasMessages(1)){
boolean inside = cTopLatitude >= nTopLatitude && cLeftLongitude <= nLeftLongitude && cRightLongitude >= nRightLongitude
&& cBottomLatitude <= nBottomLatitude;
if (!inside || czoom != zoom) {
objects = loadingBugs(nTopLatitude, nLeftLongitude, nBottomLatitude, nRightLongitude);
cTopLatitude = nTopLatitude;
cLeftLongitude = nLeftLongitude;
cRightLongitude = nRightLongitude;
cBottomLatitude = nBottomLatitude;
czoom = zoom;
view.refreshMap();
}
}
}
});
msg.what = 1;
handlerToLoop.sendMessage(msg);
}
}
protected List<OpenStreetBug> loadingBugs(double topLatitude, double leftLongitude, double bottomLatitude,double rightLongitude){
List<OpenStreetBug> bugs = new ArrayList<OpenStreetBug>();
StringBuilder b = new StringBuilder();
b.append("http://openstreetbugs.schokokeks.org/api/0.1/getBugs?");
b.append("b=").append(bottomLatitude);
b.append("&t=").append(topLatitude);
b.append("&l=").append(leftLongitude);
b.append("&r=").append(rightLongitude);
try {
URL url = new URL(b.toString());
URLConnection connection = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String st = null;
while((st = reader.readLine()) != null){
Matcher matcher = patternToParse.matcher(st);
if(matcher.find()){
OpenStreetBug bug = new OpenStreetBug();
bug.setId(Long.parseLong(matcher.group(1)));
bug.setLongitude(Double.parseDouble(matcher.group(2)));
bug.setLatitude(Double.parseDouble(matcher.group(4)));
bug.setName(matcher.group(6));
bug.setOpened(matcher.group(7).equals("0"));
bugs.add(bug);
}
}
} catch (IOException e) {
log.warn("Error loading bugs", e);
} catch (NumberFormatException e) {
log.warn("Error loading bugs", e);
} catch (RuntimeException e) {
log.warn("Error loading bugs", e);
}
return bugs;
}
@Override
public boolean onLongPressEvent(PointF point) {
return false;
}
@Override
public boolean onTouchEvent(PointF point) {
if (objects != null && !objects.isEmpty()) {
int ex = (int) point.x;
int ey = (int) point.y;
int radius = getRadiusBug(view.getZoom()) * 3 / 2;
try {
for (OpenStreetBug n : objects) {
int x = view.getRotatedMapXForPoint(n.getLatitude(), n.getLongitude());
int y = view.getRotatedMapYForPoint(n.getLatitude(), n.getLongitude());
if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) {
String format = "Bug : " + n.getName();
Toast.makeText(view.getContext(), format, Toast.LENGTH_SHORT).show();
return true;
}
}
} catch (IndexOutOfBoundsException e) {
// that's really rare case, but is much efficient than introduce synchronized block
}
}
return false;
}
public static class OpenStreetBug {
private double latitude;
private double longitude;
private String name;
private long id;
private boolean opened;
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isOpened() {
return opened;
}
public void setOpened(boolean opened) {
this.opened = opened;
}
}
}

View file

@ -16,6 +16,12 @@ public interface OsmandMapLayer {
public boolean onLongPressEvent(PointF point); public boolean onLongPressEvent(PointF point);
/**
* This method returns whether canvas should be rotated as
* map rotated before {@link #onDraw(Canvas)}.
* If the layer draws simply layer over screen (not over map)
* it should return true.
*/
public boolean drawInScreenPixels(); public boolean drawInScreenPixels();
} }

View file

@ -26,7 +26,6 @@ public class PointNavigationLayer implements OsmandMapLayer {
private void initUI() { private void initUI() {
point = new Paint(); point = new Paint();
point.setColor(Color.rgb(250, 80, 80)); point.setColor(Color.rgb(250, 80, 80));
point.setAlpha(230);
point.setAntiAlias(true); point.setAntiAlias(true);
point.setStyle(Style.FILL); point.setStyle(Style.FILL);