great refactoring

git-svn-id: https://osmand.googlecode.com/svn/trunk@32 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-05-05 09:31:54 +00:00
parent 42919e9f14
commit 7ff15e3a0c
19 changed files with 793 additions and 263 deletions

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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");

View file

@ -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;

View file

@ -10,4 +10,6 @@ public class OsmandSettings {
public static ITileSource tileSource = DefaultLauncherConstants.MAP_defaultTileSource;
public static boolean showPoiOverMap = true;
}

View file

@ -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;
@ -19,4 +23,18 @@ public class ToDoConstants {
// 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;
}

View file

@ -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)){

View file

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="**/DataExtraction*|**/MapPanel*|com/osmand/osm/io/OSMStorageWriter.java" kind="src" path="use"/>
<classpathentry excluding="**/DataExtraction*|**/MapPanel*|com/osmand/osm/io/OSMStorageWriter.java|com/osmand/LogUtil.java" kind="src" path="use"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="lib" path="lib/bzip2-20090327.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -10,12 +10,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activities.MapActivity">
<activity android:name=".activities.MapActivity" android:label="@string/app_name">
</activity>
<activity android:name=".activities.SettingsActivity"
android:label="@string/settings_activity"></activity>
<activity android:name=".activities.SettingsActivity" android:label="@string/settings_activity"></activity>
</application>
@ -26,4 +27,5 @@
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
</manifest>

Binary file not shown.

View file

@ -3,10 +3,12 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.osmand.views.OsmandMapTileView android:id="@+id/MapView" android:layout_width="wrap_content" android:layout_height="wrap_content"></com.osmand.views.OsmandMapTileView>
<com.osmand.views.PointOfView android:id="@+id/PointOfView" android:layout_width="wrap_content" android:layout_height="wrap_content"></com.osmand.views.PointOfView>
<com.osmand.views.OsmandMapTileView android:id="@+id/MapView" android:layout_width="fill_parent" android:layout_height="fill_parent"></com.osmand.views.OsmandMapTileView>
<com.osmand.views.PointOfView android:id="@+id/PointOfView" android:layout_width="fill_parent" android:layout_height="fill_parent"></com.osmand.views.PointOfView>
<com.osmand.views.POIMapLayer android:id="@+id/PoiMapLayer" android:layout_width="fill_parent" android:layout_height="fill_parent"></com.osmand.views.POIMapLayer>
<ZoomControls android:id="@+id/ZoomControls01"
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right"></ZoomControls>
<ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top|right" android:id="@+id/BackToLocation" android:background="@drawable/icon"></ImageButton>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/settings_activity" android:layout_gravity="top|left" android:id="@+id/GoToSettingsButton" ></Button>
</FrameLayout>

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<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>
<string name="map_tile_source">Map tile source</string>
<string name="map_source">Map source</string>

View file

@ -4,6 +4,7 @@
<PreferenceCategory android:title="@string/map_preferences">
<CheckBoxPreference android:key="show_gps_location_text" android:title="@string/show_location" android:summary="@string/show_gps_coordinates_text"></CheckBoxPreference>
<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>
</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

@ -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());
}
}

View file

@ -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,6 +51,13 @@ 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<Node> indexPOI;
private POIMapLayer poiMapLayer;
@Override
public void onCreate(Bundle savedInstanceState) {
@ -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,8 +120,64 @@ 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<Node> indexPOI(){
File file = new File(Environment.getExternalStorageDirectory(), POI_PATH);
DataTileManager<Node> r = new DataTileManager<Node>();
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
public void onLocationChanged(Location location) {
@ -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();
}

View file

@ -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<TileSourceTemplate> list = TileSourceManager.getKnownSourceTemplates();
String[] entries = new String[list.size()];
for(int i=0; i<list.size(); i++){
@ -52,20 +58,24 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
}
tileSourcePreference.setEntries(entries);
tileSourcePreference.setEntryValues(entries);
tileSourcePreference.setValue(DefaultLauncherConstants.MAP_defaultTileSource.getName());
tileSourcePreference.setValue(OsmandSettings.tileSource.getName());
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if(preference == showGpsLocation){
DefaultLauncherConstants.showGPSCoordinates = (Boolean) newValue;
OsmandSettings.showGPSLocationOnMap = (Boolean) newValue;
} else if(preference == showPoiOnMap){
OsmandSettings.showPoiOverMap = (Boolean) newValue;
} else if(preference == useInternetToDownloadTiles){
DefaultLauncherConstants.loadMissingImages = (Boolean) newValue;
OsmandSettings.useInternetToDownloadTiles = (Boolean) newValue;
} else if (preference == tileSourcePreference) {
String newTile = newValue.toString();
for (TileSourceTemplate t : TileSourceManager.getKnownSourceTemplates()) {
if (t.getName().equals(newTile)) {
DefaultLauncherConstants.MAP_defaultTileSource = t;
OsmandSettings.tileSource = t;
break;
}
}

View file

@ -1,15 +1,18 @@
package com.osmand.views;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import android.content.Context;
import android.graphics.Bitmap;
@ -18,14 +21,19 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import com.osmand.DefaultLauncherConstants;
import com.osmand.IMapLocationListener;
import com.osmand.LogUtil;
import com.osmand.MapTileDownloader;
import com.osmand.OsmandSettings;
import com.osmand.MapTileDownloader.DownloadRequest;
@ -33,15 +41,18 @@ import com.osmand.MapTileDownloader.IMapDownloaderCallback;
import com.osmand.map.ITileSource;
import com.osmand.osm.MapUtils;
public class OsmandMapTileView extends View implements IMapDownloaderCallback {
public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, Callback{
protected final int emptyTileDivisor = DefaultLauncherConstants.MAP_divNonLoadedImage;
protected final int maxImgCacheSize = 512;
protected final int maxImgCacheSize = 96;
protected int drawCoordinatesX = 0;
protected int drawCoordinatesY = 55;
protected static final Log log = LogFactory.getLog(OsmandMapTileView.class);
protected final int timeForDraggingAnimation = 300;
protected final int minimumDistanceForDraggingAnimation = 40;
protected static final Log log = LogUtil.getLog(OsmandMapTileView.class);
/**
* file or directory with tiles
*/
@ -66,28 +77,18 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback {
private MapTileDownloader downloader = MapTileDownloader.getInstance();
Map<String, Bitmap> cacheOfImages = new HashMap<String, Bitmap>();
// 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<String, Bitmap> cacheOfImages = new WeakHashMap<String, Bitmap>();
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) {
@ -113,9 +114,10 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback {
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,22 +263,55 @@ 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()) {
return null;
public AsyncLoadingThread asyncLoadingTiles = new AsyncLoadingThread();
public class AsyncLoadingThread extends Thread {
Map<String, DownloadRequest> requests = Collections.synchronizedMap(new LinkedHashMap<String, DownloadRequest>());
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 (cacheOfImages.get(file) == null) {
if(fileWithTiles == null || !fileWithTiles.canRead()){
return null;
}
File en = new File(fileWithTiles, file);
if (cacheOfImages.size() > maxImgCacheSize) {
ArrayList<String> list = new ArrayList<String>(cacheOfImages.keySet());
for (int i = 0; i < list.size(); i += 2) {
Bitmap bmp = cacheOfImages.remove(list.get(i));
bmp.recycle();
}
System.gc();
onLowMemory();
}
if (!downloader.isFileCurrentlyDownloaded(en)) {
if (en.exists()) {
long time = System.currentTimeMillis();
@ -238,7 +320,11 @@ public class OsmandMapTileView extends View implements IMapDownloaderCallback {
log.debug("Loaded file : " + file + " " + -(time - System.currentTimeMillis()) + " ms");
}
}
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;
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 >= 0 && i < images.length) && (j >= 0 && j < images[i].length)) {
images[i][j] = getImageFor(request.xTile, request.yTile, zoom, false);
mHandler.post(invalidateView);
(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() {
prepareImage(OsmandSettings.useInternetToDownloadTiles);
}
public void prepareImage(boolean loadNecessaryImages) {
if (loadNecessaryImages) {
if (OsmandSettings.useInternetToDownloadTiles) {
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());
int width = getWidth();
int height = getHeight();
int tileSize = getTileSize();
xStartingImage = -(int) ((xTileLeft - Math.floor(xTileLeft)) * getTileSize());
yStartingImage = -(int) ((yTileUp - Math.floor(yTileUp)) * 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);
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);
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<String> list = new ArrayList<String>(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 ?
}
}

View file

@ -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<Node> nodeManager = null;
private LatLon currentLocation = null;
private int zoomLevel = DefaultLauncherConstants.MAP_startMapZoom;
private Map<Node, Point> points = new LinkedHashMap<Node, Point>();
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<Node> 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<Node> nodeManager) {
this.nodeManager = nodeManager;
preparePoints();
}
public LatLon getCurrentLocation() {
return currentLocation;
}
public DataTileManager<Node> getNodeManager() {
return nodeManager;
}
public int getZoomLevel() {
return zoomLevel;
}
}

View file

@ -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);
}
};
}
}