diff --git a/DataExtractionOSM/src/com/osmand/Algoritms.java b/DataExtractionOSM/src/com/osmand/Algoritms.java index bab85a3f1a..7f939bb0be 100644 --- a/DataExtractionOSM/src/com/osmand/Algoritms.java +++ b/DataExtractionOSM/src/com/osmand/Algoritms.java @@ -7,14 +7,13 @@ import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * Basic algorithms that are not in jdk */ public class Algoritms { private static final int BUFFER_SIZE = 1024; - private static final Log log = LogFactory.getLog(Algoritms.class); + private static final Log log = LogUtil.getLog(Algoritms.class); public static boolean isEmpty(String s){ return s == null || s.length() == 0; diff --git a/DataExtractionOSM/src/com/osmand/LogUtil.java b/DataExtractionOSM/src/com/osmand/LogUtil.java new file mode 100644 index 0000000000..4c02b4f1fa --- /dev/null +++ b/DataExtractionOSM/src/com/osmand/LogUtil.java @@ -0,0 +1,20 @@ +package com.osmand; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * That class is replacing of standard LogFactory due to + * problems with Android implementation of LogFactory. + * See Android analog of LogUtil + * + * That class should be very simple & always use LogFactory methods, + * there is an intention to delegate all static methods to LogFactory. + */ +public class LogUtil { + + public static Log getLog(Class cl){ + return LogFactory.getLog(cl); + } + +} diff --git a/DataExtractionOSM/src/com/osmand/MapPanel.java b/DataExtractionOSM/src/com/osmand/MapPanel.java index 9d1960326e..2f30c6b099 100644 --- a/DataExtractionOSM/src/com/osmand/MapPanel.java +++ b/DataExtractionOSM/src/com/osmand/MapPanel.java @@ -32,7 +32,6 @@ import javax.swing.JPanel; import javax.swing.UIManager; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import com.osmand.DataExtraction.ExitListener; import com.osmand.MapTileDownloader.DownloadRequest; @@ -48,7 +47,7 @@ public class MapPanel extends JPanel implements IMapDownloaderCallback { private static final long serialVersionUID = 1L; - protected static final Log log = LogFactory.getLog(MapPanel.class); + protected static final Log log = LogUtil.getLog(MapPanel.class); public static Menu getMenuToChooseSource(final MapPanel panel){ Menu tiles = new Menu("Source tile"); diff --git a/DataExtractionOSM/src/com/osmand/MapTileDownloader.java b/DataExtractionOSM/src/com/osmand/MapTileDownloader.java index fe729dfcad..fbd0c2d9d7 100644 --- a/DataExtractionOSM/src/com/osmand/MapTileDownloader.java +++ b/DataExtractionOSM/src/com/osmand/MapTileDownloader.java @@ -15,12 +15,11 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; public class MapTileDownloader { private static MapTileDownloader downloader = null; - private static Log log = LogFactory.getLog(MapTileDownloader.class); + private static Log log = LogUtil.getLog(MapTileDownloader.class); private ThreadPoolExecutor threadPoolExecutor; diff --git a/DataExtractionOSM/src/com/osmand/OsmandSettings.java b/DataExtractionOSM/src/com/osmand/OsmandSettings.java index a980e40894..2fd26fc74c 100644 --- a/DataExtractionOSM/src/com/osmand/OsmandSettings.java +++ b/DataExtractionOSM/src/com/osmand/OsmandSettings.java @@ -9,5 +9,7 @@ public class OsmandSettings { public static boolean showGPSLocationOnMap = DefaultLauncherConstants.showGPSCoordinates; public static ITileSource tileSource = DefaultLauncherConstants.MAP_defaultTileSource; + + public static boolean showPoiOverMap = true; } diff --git a/DataExtractionOSM/src/com/osmand/ToDoConstants.java b/DataExtractionOSM/src/com/osmand/ToDoConstants.java index e3d3d112fe..a90a3c5c46 100644 --- a/DataExtractionOSM/src/com/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/com/osmand/ToDoConstants.java @@ -1,5 +1,6 @@ package com.osmand; + /** * This class is designed to put all to do's and link them with code. * The whole methods could be paste or just constants. @@ -9,6 +10,9 @@ package com.osmand; public class ToDoConstants { + // use unknown implementation (not written)? How to see debug msgs? + // Explanation of how it works + // The task public int CONFIG_COMMONS_LOGGING_IN_ANDROID = 1; public int SAVE_SETTINGS_IN_ANDROID_BETWEEN_SESSION = 2; @@ -18,5 +22,19 @@ public class ToDoConstants { // OsmandMapTileView.java have problem with class loading (LogFactory, MapTileDownloader) - // it is not editable in editor public int MAKE_MAP_PANEL_EDITABLE_IN_EDITOR = 4; + + // common parts : work with cache on file system & in memory + public int EXTRACT_COMMON_PARTS_FROM_MAPPANEL_AND_OSMMAPVIEW = 5; + + + public int REVISE_MAP_ACTIVITY_HOLD_ALL_ZOOM_LATLON_IN_ONEPLACE = 6; + + /** + * Resource should cache all resources & free them + * if there is no enough memory @see tile cache in tile view + * @see poi index in map activity + */ + public int INTRODUCE_RESOURCE_MANAGER = 7; + } diff --git a/DataExtractionOSM/src/com/osmand/osm/io/OsmBaseStorage.java b/DataExtractionOSM/src/com/osmand/osm/io/OsmBaseStorage.java index 2ad5a3cf18..d84c5646b6 100644 --- a/DataExtractionOSM/src/com/osmand/osm/io/OsmBaseStorage.java +++ b/DataExtractionOSM/src/com/osmand/osm/io/OsmBaseStorage.java @@ -70,6 +70,7 @@ public class OsmBaseStorage extends DefaultHandler { } SAXParserFactory factory = SAXParserFactory.newInstance(); try { + factory.setFeature("http://xml.org/sax/features/namespace-prefixes", false); return saxParser = factory.newSAXParser(); } catch (ParserConfigurationException e) { throw new IllegalStateException(e); @@ -108,6 +109,8 @@ public class OsmBaseStorage extends DefaultHandler { @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { + name = saxParser.isNamespaceAware() ? localName : name; + if(!parseStarted){ if(!ELEM_OSM.equals(name) || !supportedVersions.contains(attributes.getValue(ATTR_VERSION))){ throw new OsmVersionNotSupported(); @@ -150,6 +153,7 @@ public class OsmBaseStorage extends DefaultHandler { @Override public void endElement(String uri, String localName, String name) throws SAXException { + name = saxParser.isNamespaceAware() ? localName : name; if (ELEM_NODE.equals(name) || ELEM_WAY.equals(name) || ELEM_RELATION.equals(name)) { if(currentParsedEntity != null){ if(acceptEntityToLoad(currentParsedEntity)){ diff --git a/OsmAnd/.classpath b/OsmAnd/.classpath index 7bded3bf9f..75991137f3 100644 --- a/OsmAnd/.classpath +++ b/OsmAnd/.classpath @@ -1,8 +1,9 @@ - + + diff --git a/OsmAnd/AndroidManifest.xml b/OsmAnd/AndroidManifest.xml index 463f52dfda..00f1161be1 100644 --- a/OsmAnd/AndroidManifest.xml +++ b/OsmAnd/AndroidManifest.xml @@ -10,20 +10,22 @@ - + + - + - - - + + + + \ No newline at end of file diff --git a/OsmAnd/lib/bzip2-20090327.jar b/OsmAnd/lib/bzip2-20090327.jar new file mode 100644 index 0000000000..7721279538 Binary files /dev/null and b/OsmAnd/lib/bzip2-20090327.jar differ diff --git a/OsmAnd/res/layout/main.xml b/OsmAnd/res/layout/main.xml index f676e7ed9b..dc3932d3f9 100644 --- a/OsmAnd/res/layout/main.xml +++ b/OsmAnd/res/layout/main.xml @@ -3,10 +3,12 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> - - + + + + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 1e64dd59d9..a5a09e18fb 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,5 +1,7 @@ + Show POI on map + Show POI Choose the source of tiles Map tile source Map source diff --git a/OsmAnd/res/xml/settings_pref.xml b/OsmAnd/res/xml/settings_pref.xml index b88947b341..d354abae08 100644 --- a/OsmAnd/res/xml/settings_pref.xml +++ b/OsmAnd/res/xml/settings_pref.xml @@ -4,6 +4,7 @@ + diff --git a/OsmAnd/src/com/osmand/LogUtil.java b/OsmAnd/src/com/osmand/LogUtil.java new file mode 100644 index 0000000000..5385f26cc6 --- /dev/null +++ b/OsmAnd/src/com/osmand/LogUtil.java @@ -0,0 +1,152 @@ +package com.osmand; + +import org.apache.commons.logging.Log; + +/** + * That class is replacing of standard LogFactory due to + * problems with Android implementation of LogFactory. + * + * 1. It is impossible to replace standard LogFactory (that is hidden in android.jar) + * 2. Implementation of LogFactory always creates Logger.getLogger(String name) + * 3. + It is possible to enable logger level by calling + * Logger.getLogger("com.osmand").setLevel(Level.ALL); + * 4. Logger goes to low level android.util.Log where android.util.Log#isLoggable(String, int) is checked + * String tag -> is string of length 23 (stripped full class name) + * 5. It is impossible to set for all tags debug level (info is default) - android.util.Log#isLoggable(String, int). + * + */ +public class LogUtil { + public static String TAG = "com.osmand"; + private static class OsmandLogImplementation implements Log { + + private final String fullName; + private final String name; + + public OsmandLogImplementation(String name){ + this.fullName = name; + this.name = fullName.substring(fullName.lastIndexOf('.') + 1); + } + @Override + public void debug(Object message) { + if(isDebugEnabled()){ + android.util.Log.d(TAG, name + " " + message); + } + } + + @Override + public void debug(Object message, Throwable t) { + if(isDebugEnabled()){ + android.util.Log.d(TAG, name + " " + message, t); + } + } + + @Override + public void error(Object message) { + if(isErrorEnabled()){ + android.util.Log.e(TAG, name + " " + message); + } + } + + @Override + public void error(Object message, Throwable t) { + if(isErrorEnabled()){ + android.util.Log.e(TAG, name + " " + message, t); + } + } + + @Override + public void fatal(Object message) { + if(isFatalEnabled()){ + android.util.Log.e(TAG, name + " " + message); + } + + } + + @Override + public void fatal(Object message, Throwable t) { + if(isFatalEnabled()){ + android.util.Log.e(TAG, name + " " + message, t); + } + } + + @Override + public void info(Object message) { + if(isInfoEnabled()){ + android.util.Log.i(TAG, name + " " + message); + } + } + + @Override + public void info(Object message, Throwable t) { + if(isInfoEnabled()){ + android.util.Log.i(TAG, name + " " + message, t); + } + } + + @Override + public boolean isDebugEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); + } + + @Override + public boolean isErrorEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); + } + + @Override + public boolean isFatalEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); + } + + @Override + public boolean isInfoEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.INFO); + } + + @Override + public boolean isTraceEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); + } + + @Override + public boolean isWarnEnabled() { + return android.util.Log.isLoggable(TAG, android.util.Log.WARN); + } + + @Override + public void trace(Object message) { + if(isTraceEnabled()){ + android.util.Log.d(TAG, name + " " + message); + } + } + + @Override + public void trace(Object message, Throwable t) { + if(isTraceEnabled()){ + android.util.Log.d(TAG, name + " " + message, t); + } + } + + @Override + public void warn(Object message) { + if(isWarnEnabled()){ + android.util.Log.w(TAG, name + " " + message); + } + } + + @Override + public void warn(Object message, Throwable t) { + if(isWarnEnabled()){ + android.util.Log.w(TAG, name + " " + message, t); + } + } + } + + public static Log getLog(String name){ + return new OsmandLogImplementation(name); + } + + public static Log getLog(Class cl){ + return getLog(cl.getName()); + } +} diff --git a/OsmAnd/src/com/osmand/activities/MapActivity.java b/OsmAnd/src/com/osmand/activities/MapActivity.java index 6e68fe7609..05a9b520e3 100644 --- a/OsmAnd/src/com/osmand/activities/MapActivity.java +++ b/OsmAnd/src/com/osmand/activities/MapActivity.java @@ -1,6 +1,13 @@ package com.osmand.activities; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.tools.bzip2.CBZip2InputStream; +import org.xml.sax.SAXException; import android.app.Activity; import android.content.Intent; @@ -16,11 +23,20 @@ import android.widget.Button; import android.widget.ImageButton; import android.widget.ZoomControls; +import com.osmand.Algoritms; import com.osmand.IMapLocationListener; +import com.osmand.LogUtil; import com.osmand.OsmandSettings; import com.osmand.R; +import com.osmand.data.DataTileManager; +import com.osmand.osm.Entity; +import com.osmand.osm.LatLon; import com.osmand.osm.MapUtils; +import com.osmand.osm.Node; +import com.osmand.osm.OSMSettings.OSMTagKey; +import com.osmand.osm.io.OsmBaseStorage; import com.osmand.views.OsmandMapTileView; +import com.osmand.views.POIMapLayer; import com.osmand.views.PointOfView; public class MapActivity extends Activity implements LocationListener, IMapLocationListener { @@ -35,11 +51,18 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat private PointOfView pointOfView; + private static final String TILES_PATH = "osmand/tiles/"; + private static final String POI_PATH = "osmand/poi/"; + private static final org.apache.commons.logging.Log log = LogUtil.getLog(MapActivity.class); + + private DataTileManager indexPOI; + + private POIMapLayer poiMapLayer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + requestWindowFeature(Window.FEATURE_NO_TITLE); // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, // WindowManager.LayoutParams.FLAG_FULLSCREEN); @@ -47,7 +70,7 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat setContentView(R.layout.main); mapView = (OsmandMapTileView) findViewById(R.id.MapView); - mapView.setFileWithTiles(new File(Environment.getExternalStorageDirectory(), "osmand/tiles/")); + mapView.setFileWithTiles(new File(Environment.getExternalStorageDirectory(), TILES_PATH)); mapView.addMapLocationListener(this); ZoomControls zoomControls = (ZoomControls) findViewById(R.id.ZoomControls01); @@ -55,12 +78,14 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat @Override public void onClick(View v) { mapView.setZoom(mapView.getZoom() + 1); + poiMapLayer.setCurrentLocationAndZoom(poiMapLayer.getCurrentLocation(), mapView.getZoom()); } }); zoomControls.setOnZoomOutClickListener(new OnClickListener() { @Override public void onClick(View v) { mapView.setZoom(mapView.getZoom() - 1); + poiMapLayer.setCurrentLocationAndZoom(poiMapLayer.getCurrentLocation(), mapView.getZoom()); } }); @@ -95,7 +120,63 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE); service.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this); + indexPOI = indexPOI(); + + poiMapLayer = (POIMapLayer)findViewById(R.id.PoiMapLayer); + poiMapLayer.setNodeManager(indexPOI); + } + + private static final boolean indexPOIFlag = false; + + public DataTileManager indexPOI(){ + File file = new File(Environment.getExternalStorageDirectory(), POI_PATH); + + DataTileManager r = new DataTileManager(); + if(file.exists() && file.canRead() && indexPOIFlag){ + for(File f : file.listFiles() ){ + if(f.getName().endsWith(".bz2") || f.getName().endsWith(".osm") ){ + if(log.isDebugEnabled()){ + log.debug("Starting index POI " + f.getAbsolutePath()); + } + boolean zipped = f.getName().endsWith(".bz2"); + InputStream stream = null; + try { + OsmBaseStorage storage = new OsmBaseStorage(); + stream = new FileInputStream(f); + stream = new BufferedInputStream(stream); + if (zipped) { + if (stream.read() != 'B' || stream.read() != 'Z') { + log.error("Can't read poi file " + f.getAbsolutePath() + + "The source stream must start with the characters BZ if it is to be read as a BZip2 stream."); + continue; + } else { + stream = new CBZip2InputStream(stream); + } + } + storage.parseOSM(stream); + for(Entity e : storage.getRegisteredEntities().values()){ + if(e instanceof Node && e.getTag(OSMTagKey.AMENITY) != null){ + Node n = (Node) e; + r.registerObject(n.getLatitude(), n.getLongitude(), n); + } + } + if(log.isDebugEnabled()){ + log.debug("Finishing index POI " + f.getAbsolutePath()); + } + } catch(IOException e){ + log.error("Can't read poi file " + f.getAbsolutePath(), e); + } catch (SAXException e) { + log.error("Can't read poi file " + f.getAbsolutePath(), e); + } finally { + Algoritms.closeStream(stream); + } + } + } + } + return r; + } + @Override @@ -148,6 +229,12 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat } } + @Override + protected void onSaveInstanceState(Bundle outState) { + // TODO Auto-generated method stub + super.onSaveInstanceState(outState); + } + @Override protected void onPause() { @@ -162,16 +249,31 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat if(mapView.getMap() != OsmandSettings.tileSource){ mapView.setMap(OsmandSettings.tileSource); } + if((poiMapLayer.getVisibility() == View.VISIBLE) != OsmandSettings.showPoiOverMap){ + if(OsmandSettings.showPoiOverMap){ + poiMapLayer.setVisibility(View.VISIBLE); + } else { + poiMapLayer.setVisibility(View.INVISIBLE); + } + } + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); } @Override public void locationChanged(double newLatitude, double newLongitude, Object source) { - // when user + // when user start dragging if(source == mapView && lastKnownLocation != null){ linkLocationWithMap = false; backToLocation.setVisibility(View.VISIBLE); } + poiMapLayer.setCurrentLocationAndZoom(new LatLon(newLatitude, newLongitude), mapView.getZoom()); + validatePointOfView(); } diff --git a/OsmAnd/src/com/osmand/activities/SettingsActivity.java b/OsmAnd/src/com/osmand/activities/SettingsActivity.java index 3f05861a76..62e4341efb 100644 --- a/OsmAnd/src/com/osmand/activities/SettingsActivity.java +++ b/OsmAnd/src/com/osmand/activities/SettingsActivity.java @@ -2,12 +2,6 @@ package com.osmand.activities; import java.util.List; -import com.osmand.DefaultLauncherConstants; -import com.osmand.R; -import com.osmand.R.xml; -import com.osmand.map.TileSourceManager; -import com.osmand.map.TileSourceManager.TileSourceTemplate; - import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; @@ -16,11 +10,19 @@ import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.preference.Preference.OnPreferenceChangeListener; +import com.osmand.OsmandSettings; +import com.osmand.R; +import com.osmand.map.TileSourceManager; +import com.osmand.map.TileSourceManager.TileSourceTemplate; + public class SettingsActivity extends PreferenceActivity implements OnPreferenceChangeListener { private static final String use_internet_to_download_tiles = "use_internet_to_download_tiles"; private static final String show_gps_location_text = "show_gps_location_text"; private static final String map_tile_sources = "map_tile_sources"; + private static final String show_poi_over_map = "show_poi_over_map"; + private CheckBoxPreference showGpsLocation; + private CheckBoxPreference showPoiOnMap; private CheckBoxPreference useInternetToDownloadTiles; private ListPreference tileSourcePreference; @@ -33,6 +35,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference showGpsLocation.setOnPreferenceChangeListener(this); useInternetToDownloadTiles =(CheckBoxPreference) screen.findPreference(use_internet_to_download_tiles); useInternetToDownloadTiles.setOnPreferenceChangeListener(this); + showPoiOnMap =(CheckBoxPreference) screen.findPreference(show_poi_over_map); + showPoiOnMap.setOnPreferenceChangeListener(this); tileSourcePreference =(ListPreference) screen.findPreference(map_tile_sources); tileSourcePreference.setOnPreferenceChangeListener(this); @@ -43,8 +47,10 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference @Override protected void onResume() { super.onResume(); - useInternetToDownloadTiles.setChecked(DefaultLauncherConstants.loadMissingImages); - showGpsLocation.setChecked(DefaultLauncherConstants.showGPSCoordinates); + useInternetToDownloadTiles.setChecked(OsmandSettings.useInternetToDownloadTiles); + showGpsLocation.setChecked(OsmandSettings.showGPSLocationOnMap); + showPoiOnMap.setChecked(OsmandSettings.showPoiOverMap); + List list = TileSourceManager.getKnownSourceTemplates(); String[] entries = new String[list.size()]; for(int i=0; i cacheOfImages = new HashMap(); - // cached data to draw images - private Bitmap[][] images; - // this value is always <= 0 - private int xStartingImage = 0; - // this value is always <= 0 - private int yStartingImage = 0; - - Map cacheOfImages = new WeakHashMap(); - private PointF startDragging = null; + private boolean isStartedDragging = false; + private double startDraggingX = 0d; + private double startDraggingY = 0d; + private PointF initStartDragging = null; Paint paintGrayFill; Paint paintWhiteFill; Paint paintBlack; - final Handler mHandler = new Handler(); + private AnimatedDragging animatedDraggingThread; - // Create runnable for posting - final Runnable invalidateView = new Runnable() { - public void run() { - invalidate(); - } - }; public OsmandMapTileView(Context context, AttributeSet attrs) { @@ -112,10 +113,11 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { paintBlack = new Paint(); paintBlack.setStyle(Style.STROKE); paintBlack.setColor(Color.BLACK); - - prepareImage(); + setClickable(true); + getHolder().addCallback(this); downloader.setDownloaderCallback(this); + asyncLoadingTiles.start(); } @@ -124,31 +126,103 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { } - public void dragTo(PointF p){ - double dx = (startDragging.x - (double)p.x)/getTileSize(); - double dy = (startDragging.y - (double)p.y)/getTileSize(); + public void dragTo(double fromX, double fromY, double toX, double toY){ + double dx = (fromX - toX)/getTileSize(); + double dy = (fromY - toY)/getTileSize(); this.latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + dy); this.longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + dx); prepareImage(); - fireMapLocationListeners(this); + // TODO +// fireMapLocationListeners(this); } + public class AnimatedDragging extends Thread { + private float curX; + private float curY; + private float vx; + private float vy; + private float ax; + private float ay; + private byte dirX; + private byte dirY; + private long time = System.currentTimeMillis(); + private boolean stopped; + private final float a = 0.0005f; + + public AnimatedDragging(float dTime, float startX, float startY, float endX, float endY) { + vx = Math.abs((endX - startX)/dTime); + vy = Math.abs((endY - startY)/dTime); + dirX = (byte) (endX > startX ? 1 : -1); + dirY = (byte) (endY > startY ? 1 : -1); + ax = vx * a; + ay = vy * a; + } + + @Override + public void run() { + try { + while ((vx > 0 || vy > 0) && !isStartedDragging && !stopped) { + sleep((long) (40d/(Math.max(vx, vy)+0.45))); + long curT = System.currentTimeMillis(); + int dt = (int) (curT - time); + float newX = vx > 0 ? curX + dirX * vx * dt : curX; + float newY = vy > 0 ? curY + dirY * vy * dt : curY; + if(!isStartedDragging){ + dragTo(curX, curY, newX, newY); + } + vx -= ax * dt; + vy -= ay * dt; + time = curT; + curX = newX; + curY = newY; + } + } catch (InterruptedException e) { + } + animatedDraggingThread = null; + } + + public void stopEvaluation(){ + stopped = true; + } + + } + + + @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN) { - if(startDragging == null){ - startDragging = new PointF(event.getX(), event.getY()); + if(animatedDraggingThread != null){ + animatedDraggingThread.stopEvaluation(); + } + if(!isStartedDragging){ + startDraggingX = event.getX(); + startDraggingY = event.getY(); + isStartedDragging = true; + initStartDragging = new PointF(event.getX(), event.getY()); } } else if(event.getAction() == MotionEvent.ACTION_UP) { - if(startDragging != null){ - dragTo(new PointF(event.getX(), event.getY())); - startDragging = null; + if(isStartedDragging){ + dragTo(startDraggingX, startDraggingY, event.getX(), event.getY()); + if(event.getEventTime() - event.getDownTime() < timeForDraggingAnimation && + Math.abs(event.getX() - initStartDragging.x) + Math.abs(event.getY() - initStartDragging.y) > minimumDistanceForDraggingAnimation){ + float timeDist = (int) (event.getEventTime() - event.getDownTime()); + if(timeDist < 20){ + timeDist = 20; + } + animatedDraggingThread = new AnimatedDragging(timeDist, initStartDragging.x, initStartDragging.y, + event.getX(), event.getY()); + isStartedDragging = false; + animatedDraggingThread.start(); + } + isStartedDragging = false; + } } else if(event.getAction() == MotionEvent.ACTION_MOVE) { - if(startDragging != null){ - PointF p = new PointF(event.getX(), event.getY()); - dragTo(p); - startDragging = p; + if(isStartedDragging){ + dragTo(startDraggingX, startDraggingY, event.getX(), event.getY()); + startDraggingX = event.getX(); + startDraggingY = event.getY(); } } return super.onTouchEvent(event); @@ -178,31 +252,6 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { } } - @Override - protected void onDraw(Canvas canvas) { - canvas.drawRect(0,0, getWidth(), getHeight(), paintWhiteFill); - if (images != null) { - for (int i = 0; i < images.length; i++) { - for (int j = 0; j < images[i].length; j++) { - if (images[i][j] == null) { - drawEmptyTile(canvas, i*getTileSize()+xStartingImage, j * getTileSize() + yStartingImage); - } else { - canvas.drawBitmap(images[i][j], i * getTileSize() + xStartingImage, j * getTileSize() + yStartingImage, null); - } - } - } - } - canvas.drawCircle(getWidth()/2, getHeight()/2, 3, paintBlack); - canvas.drawCircle(getWidth()/2, getHeight()/2, 6, paintBlack); - if (OsmandSettings.showGPSLocationOnMap) { - canvas.drawText(MessageFormat.format("Lat : {0}, lon : {1}, zoom : {2}", latitude, longitude, zoom), - drawCoordinatesX, drawCoordinatesY, paintBlack); - } - } - - - - @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { prepareImage(); @@ -214,31 +263,68 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { return map.getName() +"/"+zoom+"/"+(x) +"/"+y+ext+".tile"; } - public Bitmap getImageFor(int x, int y, int zoom, boolean loadIfNeeded) { - if (map == null || fileWithTiles == null || !fileWithTiles.canRead()) { + public AsyncLoadingThread asyncLoadingTiles = new AsyncLoadingThread(); + + public class AsyncLoadingThread extends Thread { + Map requests = Collections.synchronizedMap(new LinkedHashMap()); + + public AsyncLoadingThread(){ + super("Async loading tiles"); + } + + @Override + public void run() { + while(true){ + try { + boolean update = false; + while(!requests.isEmpty()){ + String f = requests.keySet().iterator().next(); + DownloadRequest r = requests.remove(f); + // TODO last param + getImageForTile(r.xTile, r.yTile, r.zoom, OsmandSettings.useInternetToDownloadTiles); + update = true; + } + if(update){ + prepareImage(); + } + sleep(350); + } catch (InterruptedException e) { + log.error(e); + } catch (RuntimeException e){ + log.error(e); + } + } + } + + public void requestToLoadImage(String s, DownloadRequest req){ + requests.put(s, req); + + } + }; + + private Bitmap getImageForTile(int x, int y, int zoom, boolean loadIfNeeded){ + String file = getFileForImage(x, y, zoom, map.getTileFormat()); + if(fileWithTiles == null || !fileWithTiles.canRead()){ return null; } - - String file = getFileForImage(x, y, zoom, map.getTileFormat()); - if (cacheOfImages.get(file) == null) { - File en = new File(fileWithTiles, file); - if (cacheOfImages.size() > maxImgCacheSize) { - ArrayList list = new ArrayList(cacheOfImages.keySet()); - for (int i = 0; i < list.size(); i += 2) { - Bitmap bmp = cacheOfImages.remove(list.get(i)); - bmp.recycle(); + File en = new File(fileWithTiles, file); + if (cacheOfImages.size() > maxImgCacheSize) { + onLowMemory(); + } + + if (!downloader.isFileCurrentlyDownloaded(en)) { + if (en.exists()) { + long time = System.currentTimeMillis(); + cacheOfImages.put(file, BitmapFactory.decodeFile(en.getAbsolutePath())); + if (log.isDebugEnabled()) { + log.debug("Loaded file : " + file + " " + -(time - System.currentTimeMillis()) + " ms"); } - System.gc(); - } - if (!downloader.isFileCurrentlyDownloaded(en)) { - if (en.exists()) { - long time = System.currentTimeMillis(); - cacheOfImages.put(file, BitmapFactory.decodeFile(en.getAbsolutePath())); - if (log.isDebugEnabled()) { - log.debug("Loaded file : " + file + " " + -(time - System.currentTimeMillis()) + " ms"); - } - } - if(loadIfNeeded && cacheOfImages.get(file) == null){ + } + + if(loadIfNeeded && cacheOfImages.get(file) == null){ + ConnectivityManager mgr = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = mgr.getActiveNetworkInfo(); + if (info != null && info.isConnected()) { String urlToLoad = map.getUrlToLoad(x, y, zoom); if (urlToLoad != null) { downloader.requestToDownload(urlToLoad, new DownloadRequest(en, x, y, zoom)); @@ -246,55 +332,110 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { } } } - return cacheOfImages.get(file); } - @Override + public Bitmap getImageFor(int x, int y, int zoom, boolean loadIfNeeded) { + if (map == null) { + return null; + } + String file = getFileForImage(x, y, zoom, map.getTileFormat()); + if (cacheOfImages.get(file) == null && loadIfNeeded) { + // TODO use loadIfNeeded + asyncLoadingTiles.requestToLoadImage(file, new DownloadRequest(null, x, y, zoom)); + } + return cacheOfImages.get(file); + } + + public void tileDownloaded(String dowloadedUrl, DownloadRequest request) { - int tileSize = getTileSize(); - double xTileLeft = getXTile() - getWidth() / (2d * tileSize); - double yTileUp = getYTile() - getHeight() / (2d * tileSize); - int i = request.xTile - (int)xTileLeft; - int j = request.yTile - (int)yTileUp; - if(request.zoom == this.zoom && - (i >= 0 && i < images.length) && (j >= 0 && j < images[i].length)) { - images[i][j] = getImageFor(request.xTile, request.yTile, zoom, false); - mHandler.post(invalidateView); - } - } - - public void prepareImage(){ - prepareImage(OsmandSettings.useInternetToDownloadTiles); - } - - public void prepareImage(boolean loadNecessaryImages) { - if (loadNecessaryImages) { - downloader.refuseAllPreviousRequests(); - } - double xTileLeft = getXTile() - getWidth() / (2d * getTileSize()); - double xTileRight = getXTile() + getWidth() / (2d * getTileSize()); - double yTileUp = getYTile() - getHeight() / (2d * getTileSize()); - double yTileDown = getYTile() + getHeight() / (2d * getTileSize()); - - xStartingImage = -(int) ((xTileLeft - Math.floor(xTileLeft)) * getTileSize()); - yStartingImage = -(int) ((yTileUp - Math.floor(yTileUp)) * getTileSize()); - - int tileXCount = ((int) xTileRight - (int) xTileLeft + 1); - int tileYCount = ((int) yTileDown - (int) yTileUp + 1); - images = new Bitmap[tileXCount][tileYCount]; - for (int i = 0; i < images.length; i++) { - for (int j = 0; j < images[i].length; j++) { - images[i][j] = getImageFor((int) xTileLeft + i, (int) yTileUp + j, zoom, loadNecessaryImages); + int xTileLeft = (int) Math.floor(getXTile() - getWidth() / (2d * getTileSize())); + int yTileUp = (int) Math.floor(getYTile() - getHeight() / (2d * getTileSize())); + int startingX = (int) ((xTileLeft - getXTile()) * getTileSize() + getWidth() / 2); + int startingY = (int) ((yTileUp - getYTile()) * getTileSize() + getHeight() / 2); + int i = (request.xTile - xTileLeft) * getTileSize() + startingX; + int j = (request.yTile - yTileUp) * getTileSize() + startingY; + if (request.zoom == this.zoom && + (i + getTileSize() >= 0 && i < getWidth()) && (j + getTileSize() >= 0 && j < getHeight())) { + SurfaceHolder holder = getHolder(); + synchronized (holder) { + Canvas canvas = holder.lockCanvas(new Rect(i, j, getTileSize() + i, getTileSize() + j)); + if (canvas != null) { + try { + Bitmap bmp = getImageFor(request.xTile, request.yTile, zoom, true); + if (bmp == null) { + drawEmptyTile(canvas, i, j); + } else { + canvas.drawBitmap(bmp, i, j, null); + } + drawOverMap(canvas); + } finally { + holder.unlockCanvasAndPost(canvas); + } + } + } + } + } + private MessageFormat formatOverMap = new MessageFormat("Lat : {0}, lon : {1}, zoom : {2}, mem : {3}"); + java.util.Formatter formatterOMap = new java.util.Formatter(); + private ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + private void drawOverMap(Canvas canvas){ + canvas.drawCircle(getWidth() / 2, getHeight() / 2, 3, paintBlack); + canvas.drawCircle(getWidth() / 2, getHeight() / 2, 6, paintBlack); + if (OsmandSettings.showGPSLocationOnMap) { + float f= (Runtime.getRuntime().totalMemory())/ 1e6f; + formatterOMap = new Formatter(); + canvas.drawText(formatterOMap.format("Lat : %.3f, lon : %.3f, zoom : %d, mem : %.3f", latitude, longitude, zoom, f).toString(), + drawCoordinatesX, drawCoordinatesY, paintBlack); +// canvas.drawText(formatOverMap.format(new Object[]{latitude, longitude, zoom, f}), drawCoordinatesX, +// drawCoordinatesY, paintBlack); + } + } + + public void prepareImage() { + if (OsmandSettings.useInternetToDownloadTiles) { + downloader.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); + + SurfaceHolder holder = getHolder(); + synchronized (holder) { + Canvas canvas = holder.lockCanvas(); + if (canvas != null) { + try { + for (int i = 0; i * tileSize + startingX < width; i++) { + for (int j = 0; j * tileSize + startingY < height; j++) { + Bitmap bmp = getImageFor(xTileLeft + i, yTileUp + j, zoom, true); + if (bmp == null) { + drawEmptyTile(canvas, i * tileSize + startingX, j * tileSize + startingY); + } else { + canvas.drawBitmap(bmp, i * tileSize + startingX, j * tileSize + startingY, null); + } + } + } + drawOverMap(canvas); + } finally { + holder.unlockCanvasAndPost(canvas); + } } } - invalidate(); } public void setZoom(int zoom){ if (map == null || (map.getMaximumZoomSupported() >= zoom && map.getMinimumZoomSupported() <= zoom)) { + if(animatedDraggingThread != null){ + animatedDraggingThread.stopEvaluation(); + } this.zoom = zoom; prepareImage(); } @@ -344,6 +485,19 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { } + public void onLowMemory(){ + log.info("On low memory : cleaning tiles - size = " + cacheOfImages.size()); + ArrayList list = new ArrayList(cacheOfImages.keySet()); + // remove first images (as we think they are older) + for (int i = 0; i < list.size()/2; i ++) { + Bitmap bmp = cacheOfImages.remove(list.get(i)); + if(bmp != null){ + bmp.recycle(); + } + } + System.gc(); + } + public void addMapLocationListener(IMapLocationListener l){ listeners.add(l); @@ -359,7 +513,21 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback { } } + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + prepareImage(); + } + @Override + public void surfaceCreated(SurfaceHolder holder) { + prepareImage(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + // TODO clear cache ? + + } } diff --git a/OsmAnd/src/com/osmand/views/POIMapLayer.java b/OsmAnd/src/com/osmand/views/POIMapLayer.java new file mode 100644 index 0000000000..c64a2c72ea --- /dev/null +++ b/OsmAnd/src/com/osmand/views/POIMapLayer.java @@ -0,0 +1,154 @@ +package com.osmand.views; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Toast; + +import com.osmand.DefaultLauncherConstants; +import com.osmand.LogUtil; +import com.osmand.OsmandSettings; +import com.osmand.data.DataTileManager; +import com.osmand.map.ITileSource; +import com.osmand.osm.LatLon; +import com.osmand.osm.MapUtils; +import com.osmand.osm.Node; +import com.osmand.osm.OSMSettings.OSMTagKey; + +public class POIMapLayer extends View { + private DataTileManager nodeManager = null; + private LatLon currentLocation = null; + private int zoomLevel = DefaultLauncherConstants.MAP_startMapZoom; + private Map points = new LinkedHashMap(); + private Paint pointUI; + private static final int radiusClick = 16; + private Toast previousShownToast =null; + private final static Log log = LogUtil.getLog(POIMapLayer.class); + + + public POIMapLayer(Context context, AttributeSet attrs) { + super(context, attrs); + initUI(); + } + + public POIMapLayer(Context context) { + super(context); + initUI(); + } + + private void initUI() { + pointUI = new Paint(); + pointUI.setColor(Color.CYAN); + pointUI.setAlpha(150); + pointUI.setAntiAlias(true); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + for(Node n : points.keySet()){ + Point p = points.get(n); + canvas.drawCircle(p.x, p.y, radiusClick/2, pointUI); + } + } + + public void preparePoints() { + points.clear(); + if (nodeManager != null && currentLocation != null) { + double tileNumberX = MapUtils.getTileNumberX(zoomLevel, currentLocation.getLongitude()); + double tileNumberY = MapUtils.getTileNumberY(zoomLevel, currentLocation.getLatitude()); + double xTileLeft = tileNumberX - getWidth() / (2d * getTileSize()); + double xTileRight = tileNumberX + getWidth() / (2d * getTileSize()); + double yTileUp = tileNumberY - getHeight() / (2d * getTileSize()); + double yTileDown = tileNumberY + getHeight() / (2d * getTileSize()); + + List objects = nodeManager.getObjects(MapUtils.getLatitudeFromTile(zoomLevel, yTileUp), + MapUtils.getLongitudeFromTile(zoomLevel, xTileLeft), + MapUtils.getLatitudeFromTile(zoomLevel, yTileDown), + MapUtils.getLongitudeFromTile(zoomLevel, xTileRight)); + for (Node o : objects) { + double tileX = MapUtils.getTileNumberX(zoomLevel, o.getLongitude()); + int x = (int) ((tileX - xTileLeft) * getTileSize()); + double tileY = MapUtils.getTileNumberY(zoomLevel, o.getLatitude()); + int y = (int) ((tileY - yTileUp) * getTileSize()); + points.put(o, new Point(x, y)); + } + } + invalidate(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if(event.getAction() == MotionEvent.ACTION_DOWN) { + + if(previousShownToast != null){ + previousShownToast.cancel(); + previousShownToast = null; + } + int x = (int) event.getX(); + int y = (int) event.getY(); + + for(Node n : points.keySet()){ + Point p = points.get(n); + if(Math.abs(p.x - x) <= radiusClick && Math.abs(p.y - y) <= radiusClick){ + StringBuilder b = new StringBuilder(); + b.append("This is an amenity : \n"); + b.append("type - ").append(n.getTag(OSMTagKey.AMENITY)).append("\n"); + if(n.getTag(OSMTagKey.NAME) != null){ + b.append("name - ").append(n.getTag(OSMTagKey.NAME)).append("\n"); + } + b.append("id - ").append(n.getId()); + + previousShownToast = Toast.makeText(getContext(), b.toString(), Toast.LENGTH_SHORT); + previousShownToast.show(); + // TODO use precision + log.debug("Precision is " + event.getXPrecision()); + return true; + } + } + } + return super.onTouchEvent(event); + } + + public void setCurrentLocationAndZoom(LatLon currentLocation, int zoom) { + this.currentLocation = currentLocation; + this.zoomLevel = zoom; + preparePoints(); + } + + public int getTileSize(){ + ITileSource source = OsmandSettings.tileSource; + return source == null ? 256 : source.getTileSize(); + + } + + public void setNodeManager(DataTileManager nodeManager) { + this.nodeManager = nodeManager; + preparePoints(); + } + + public LatLon getCurrentLocation() { + return currentLocation; + } + + public DataTileManager getNodeManager() { + return nodeManager; + } + + + public int getZoomLevel() { + return zoomLevel; + } + +} diff --git a/OsmAnd/src/org/apache/commons/logging/LogFactory.java b/OsmAnd/src/org/apache/commons/logging/LogFactory.java deleted file mode 100644 index 9582683557..0000000000 --- a/OsmAnd/src/org/apache/commons/logging/LogFactory.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.apache.commons.logging; - -public class LogFactory { - public static String TAG = "com.osmand"; - - public static Log getLog(Class cl){ - final String name = cl.getName(); - return new Log() { - @Override - public void debug(Object message) { - android.util.Log.d(TAG, name + " " + message); - - } - - @Override - public void debug(Object message, Throwable t) { - android.util.Log.d(TAG, name + " " + message, t); - } - - @Override - public void error(Object message) { - android.util.Log.e(TAG, name + " " + message); - } - - @Override - public void error(Object message, Throwable t) { - android.util.Log.e(TAG, name + " " + message, t); - } - - @Override - public void fatal(Object message) { - android.util.Log.e(TAG, name + " " + message); - } - - @Override - public void fatal(Object message, Throwable t) { - android.util.Log.e(TAG, name + " " + message, t); - } - - @Override - public void info(Object message) { - android.util.Log.i(TAG, name + " " + message); - } - - @Override - public void info(Object message, Throwable t) { - android.util.Log.i(TAG, name + " " + message, t); - - } - - @Override - public boolean isDebugEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); - } - - @Override - public boolean isErrorEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); - } - - @Override - public boolean isFatalEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.ERROR); - } - - @Override - public boolean isInfoEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.INFO); - } - - @Override - public boolean isTraceEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG); - } - - @Override - public boolean isWarnEnabled() { - return android.util.Log.isLoggable(TAG, android.util.Log.WARN); - } - - @Override - public void trace(Object message) { - android.util.Log.d(TAG, name + " " + message); - } - - @Override - public void trace(Object message, Throwable t) { - android.util.Log.d(TAG, name + " " + message, t); - } - - @Override - public void warn(Object message) { - android.util.Log.w(TAG, name + " " + message); - - } - - @Override - public void warn(Object message, Throwable t) { - android.util.Log.w(TAG, name + " " + message, t); - } - - }; - } - -}