diff --git a/DataExtractionOSM/src/com/osmand/ToDoConstants.java b/DataExtractionOSM/src/com/osmand/ToDoConstants.java index a324738139..4c60b1fa72 100644 --- a/DataExtractionOSM/src/com/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/com/osmand/ToDoConstants.java @@ -11,7 +11,7 @@ public class ToDoConstants { */ 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) // 16. Support open street bugs api. // 20. Implement save track/route to gpx (?) @@ -22,14 +22,13 @@ public class ToDoConstants { // 31. Translation. // 32. Introduce POI predefined filters (car filter(other-fuel, transportation-car_wash, show-car) and others) // 33. Build transport locations (investigate) -// 34. Investigate routing (bicycle, car) +// 34. Investigate routing (bicycle, car) +// 35. Enable trackball navigation in android - // FIXME Bugs Android : - // 10. Notification is gone after clear all notifications (TODO test) - - // TODO swing NOT in release 0.1 + // TODO swing // 1. Download tiles without using dir tiles + // 2. Internal (Simplify MapPanel - introduce layers for it) // DONE ANDROID : diff --git a/DataExtractionOSM/src/com/osmand/data/preparation/MapTileDownloader.java b/DataExtractionOSM/src/com/osmand/data/preparation/MapTileDownloader.java index 5aafb44b71..fa0eadea19 100644 --- a/DataExtractionOSM/src/com/osmand/data/preparation/MapTileDownloader.java +++ b/DataExtractionOSM/src/com/osmand/data/preparation/MapTileDownloader.java @@ -24,7 +24,7 @@ import com.osmand.LogUtil; public class MapTileDownloader { // Application constants public static String APP_NAME = "OsmAnd"; - public static String APP_VERSION = "0.1"; + public static String APP_VERSION = "0.2"; // Download manager tile settings @@ -131,7 +131,15 @@ public class MapTileDownloader { 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){ diff --git a/DataExtractionOSM/src/com/osmand/swing/OsmExtractionUI.java b/DataExtractionOSM/src/com/osmand/swing/OsmExtractionUI.java index 39784d1f02..7ce77b35cb 100644 --- a/DataExtractionOSM/src/com/osmand/swing/OsmExtractionUI.java +++ b/DataExtractionOSM/src/com/osmand/swing/OsmExtractionUI.java @@ -104,7 +104,9 @@ public class OsmExtractionUI implements IMapLocationListener { Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(){ @Override 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); } }); diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index e4a6558172..9cae2e27cc 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,5 +1,7 @@ + Show open street bugs on map + Show open street bugs List of favourite points Add to Favourites Select use native or english names @@ -21,7 +23,7 @@ Map View 3D Rotate map to bearing of your direction Rotate map - Show POI on map + Show POI on map (zoom > 15) Show POI Choose the source of tiles: Map tile source diff --git a/OsmAnd/res/xml/settings_pref.xml b/OsmAnd/res/xml/settings_pref.xml index c5729b779e..547b5a01d0 100644 --- a/OsmAnd/res/xml/settings_pref.xml +++ b/OsmAnd/res/xml/settings_pref.xml @@ -9,7 +9,7 @@ - + diff --git a/OsmAnd/src/com/osmand/OsmandSettings.java b/OsmAnd/src/com/osmand/OsmandSettings.java index 4bc2e1970a..92fb509b16 100644 --- a/OsmAnd/src/com/osmand/OsmandSettings.java +++ b/OsmAnd/src/com/osmand/OsmandSettings.java @@ -33,6 +33,14 @@ public class OsmandSettings { 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 public static final String SHOW_VIEW_ANGLE = "show_view_angle"; public static boolean isShowingViewAngle(Context ctx){ diff --git a/OsmAnd/src/com/osmand/activities/MapActivity.java b/OsmAnd/src/com/osmand/activities/MapActivity.java index f1d53f2645..0dbd0eae9f 100644 --- a/OsmAnd/src/com/osmand/activities/MapActivity.java +++ b/OsmAnd/src/com/osmand/activities/MapActivity.java @@ -43,6 +43,7 @@ import com.osmand.data.preparation.MapTileDownloader; import com.osmand.map.IMapLocationListener; import com.osmand.osm.LatLon; import com.osmand.views.MapInfoLayer; +import com.osmand.views.OsmBugsLayer; import com.osmand.views.OsmandMapTileView; import com.osmand.views.POIMapLayer; import com.osmand.views.PointLocationLayer; @@ -61,11 +62,14 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat private PointNavigationLayer navigationLayer; private POIMapLayer poiMapLayer; private MapInfoLayer mapInfoLayer; + private OsmBugsLayer osmBugsLayer; private WakeLock wakeLock; private boolean sensorRegistered = false; private Handler sensorHandler = new Handler(); + + 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_LON = "POINT_NAVIGATE_LON"; @@ -95,6 +99,7 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat mapView.addLayer(locationLayer); mapInfoLayer = new MapInfoLayer(this); mapView.addLayer(mapInfoLayer); + osmBugsLayer = new OsmBugsLayer(); @@ -305,6 +310,14 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat 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); service.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 0, this); diff --git a/OsmAnd/src/com/osmand/activities/SettingsActivity.java b/OsmAnd/src/com/osmand/activities/SettingsActivity.java index adc6f156d3..f512f2269f 100644 --- a/OsmAnd/src/com/osmand/activities/SettingsActivity.java +++ b/OsmAnd/src/com/osmand/activities/SettingsActivity.java @@ -27,6 +27,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference private CheckBoxPreference showViewAngle; private ListPreference positionOnMap; private CheckBoxPreference useEnglishNames; + private CheckBoxPreference showOsmBugs; @Override public void onCreate(Bundle savedInstanceState) { @@ -41,6 +42,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference rotateMapToBearing.setOnPreferenceChangeListener(this); showViewAngle =(CheckBoxPreference) screen.findPreference(OsmandSettings.SHOW_VIEW_ANGLE); showViewAngle.setOnPreferenceChangeListener(this); + showOsmBugs =(CheckBoxPreference) screen.findPreference(OsmandSettings.SHOW_OSM_BUGS); + showOsmBugs.setOnPreferenceChangeListener(this); useEnglishNames =(CheckBoxPreference) screen.findPreference(OsmandSettings.USE_ENGLISH_NAMES); useEnglishNames.setOnPreferenceChangeListener(this); @@ -59,6 +62,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference showPoiOnMap.setChecked(OsmandSettings.isShowingPoiOverMap(this)); rotateMapToBearing.setChecked(OsmandSettings.isRotateMapToBearing(this)); showViewAngle.setChecked(OsmandSettings.isShowingViewAngle(this)); + showOsmBugs.setChecked(OsmandSettings.isShowingOsmBugs(this)); useEnglishNames.setChecked(OsmandSettings.usingEnglishNames(this)); String[] e = new String[] { "Center", "Bottom" }; positionOnMap.setEntryValues(e); @@ -99,6 +103,9 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference } else if(preference == showViewAngle){ edit.putBoolean(OsmandSettings.SHOW_VIEW_ANGLE, (Boolean) newValue); edit.commit(); + } else if(preference == showOsmBugs){ + edit.putBoolean(OsmandSettings.SHOW_OSM_BUGS, (Boolean) newValue); + edit.commit(); } else if(preference == positionOnMap){ edit.putInt(OsmandSettings.POSITION_ON_MAP, positionOnMap.findIndexOfValue((String) newValue)); edit.commit(); diff --git a/OsmAnd/src/com/osmand/views/OsmBugsLayer.java b/OsmAnd/src/com/osmand/views/OsmBugsLayer.java new file mode 100644 index 0000000000..aa7c6f89ef --- /dev/null +++ b/OsmAnd/src/com/osmand/views/OsmBugsLayer.java @@ -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 objects = new ArrayList(); + 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 loadingBugs(double topLatitude, double leftLongitude, double bottomLatitude,double rightLongitude){ + List bugs = new ArrayList(); + 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; + } + + + } + +} diff --git a/OsmAnd/src/com/osmand/views/OsmandMapLayer.java b/OsmAnd/src/com/osmand/views/OsmandMapLayer.java index 5c28314e1d..e391e66c37 100644 --- a/OsmAnd/src/com/osmand/views/OsmandMapLayer.java +++ b/OsmAnd/src/com/osmand/views/OsmandMapLayer.java @@ -16,6 +16,12 @@ public interface OsmandMapLayer { 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(); } diff --git a/OsmAnd/src/com/osmand/views/PointNavigationLayer.java b/OsmAnd/src/com/osmand/views/PointNavigationLayer.java index 62278bb672..b50614fc26 100644 --- a/OsmAnd/src/com/osmand/views/PointNavigationLayer.java +++ b/OsmAnd/src/com/osmand/views/PointNavigationLayer.java @@ -26,7 +26,6 @@ public class PointNavigationLayer implements OsmandMapLayer { private void initUI() { point = new Paint(); point.setColor(Color.rgb(250, 80, 80)); - point.setAlpha(230); point.setAntiAlias(true); point.setStyle(Style.FILL);