reintroduce amenity layer (with layer framework for map)

fixing bugs for map
introduce Resource Manager & proper indexing poi
trying to fix OutOfMemoryException

git-svn-id: https://osmand.googlecode.com/svn/trunk@35 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-05-05 22:29:58 +00:00
parent 15bcbac26a
commit bbd56254ae
19 changed files with 939 additions and 669 deletions

View file

@ -47,6 +47,7 @@ import org.apache.tools.bzip2.CBZip2InputStream;
import org.apache.tools.bzip2.CBZip2OutputStream;
import org.xml.sax.SAXException;
import com.osmand.data.Amenity;
import com.osmand.data.City;
import com.osmand.data.DataTileManager;
import com.osmand.data.Region;
@ -132,7 +133,7 @@ public class DataExtraction implements IMapLocationListener {
// preloaded data
final List<Node> places = new ArrayList<Node>();
final List<Entity> buildings = new ArrayList<Entity>();
final List<Node> amenities = new ArrayList<Node>();
final List<Amenity> amenities = new ArrayList<Amenity>();
// highways count
final List<Way> mapWays = new ArrayList<Way>();
@ -150,20 +151,8 @@ public class DataExtraction implements IMapLocationListener {
@Override
public boolean acceptNodeToLoad(Node n) {
if (n.getTag(OSMTagKey.AMENITY) != null) {
amenities.add(n);
} else if (n.getTag(OSMTagKey.SHOP) != null) {
// TODO temp solution
n.putTag(OSMTagKey.AMENITY.getValue(), OSMTagKey.SHOP.getValue());
amenities.add(n);
} else if (n.getTag(OSMTagKey.LEISURE) != null) {
// TODO temp solution
n.putTag(OSMTagKey.AMENITY.getValue(), OSMTagKey.LEISURE.getValue());
amenities.add(n);
} else if (n.getTag(OSMTagKey.TOURISM) != null) {
// TODO temp solution
n.putTag(OSMTagKey.AMENITY.getValue(), OSMTagKey.TOURISM.getValue());
amenities.add(n);
if(Amenity.isAmenity(n)){
amenities.add(new Amenity(n));
}
if (n.getTag(OSMTagKey.PLACE) != null) {
places.add(n);
@ -228,9 +217,9 @@ public class DataExtraction implements IMapLocationListener {
}
DataTileManager<LatLon> amenitiesManager = new DataTileManager<LatLon>();
for(Node node : amenities){
country.registerAmenity(node);
amenitiesManager.registerObject(node.getLatitude(), node.getLongitude(), node.getLatLon());
for(Amenity a: amenities){
country.registerAmenity(a);
amenitiesManager.registerObject(a.getNode().getLatitude(), a.getNode().getLongitude(), a.getNode().getLatLon());
}
@ -249,7 +238,9 @@ public class DataExtraction implements IMapLocationListener {
runUI(country);
List<Long> interestedObjects = new ArrayList<Long>();
// MapUtils.addIdsToList(places, interestedObjects);
MapUtils.addIdsToList(amenities, interestedObjects);
for(Amenity a : amenities){
interestedObjects.add(a.getNode().getId());
}
// MapUtils.addIdsToList(mapWays, interestedObjects);
// MapUtils.addIdsToList(buildings, interestedObjects);
if (DefaultLauncherConstants.writeTestOsmFile != null) {
@ -440,21 +431,21 @@ public class DataExtraction implements IMapLocationListener {
@Override
public void locationChanged(final double newLatitude, final double newLongitude, Object source){
Region reg = (Region) amenitiesTree.getUserObject();
List<Node> closestAmenities = reg.getClosestAmenities(newLatitude, newLongitude);
Collections.sort(closestAmenities, new Comparator<Node>(){
List<Amenity> closestAmenities = reg.getClosestAmenities(newLatitude, newLongitude);
Collections.sort(closestAmenities, new Comparator<Amenity>(){
@Override
public int compare(Node o1, Node o2) {
return Double.compare(MapUtils.getDistance(o1, newLatitude, newLongitude),
MapUtils.getDistance(o2, newLatitude, newLongitude));
public int compare(Amenity o1, Amenity o2) {
return Double.compare(MapUtils.getDistance(o1.getNode(), newLatitude, newLongitude),
MapUtils.getDistance(o2.getNode(), newLatitude, newLongitude));
}
});
Map<String, List<Node>> filter = new TreeMap<String, List<Node>>();
for(Node n : closestAmenities){
String type = n.getTag(OSMTagKey.AMENITY);
Map<String, List<Amenity>> filter = new TreeMap<String, List<Amenity>>();
for(Amenity n : closestAmenities){
String type = n.getType().toString();
if(!filter.containsKey(type)){
filter.put(type, new ArrayList<Node>());
filter.put(type, new ArrayList<Amenity>());
}
filter.get(type).add(n);
}
@ -470,11 +461,9 @@ public class DataExtraction implements IMapLocationListener {
for(int i=0; i<15 && i < closestAmenities.size(); i++){
Node n = closestAmenities.get(i);
String type = n.getTag(OSMTagKey.AMENITY);
String name = n.getTag(OSMTagKey.NAME);
int dist = (int) (MapUtils.getDistance(n, newLatitude, newLongitude));
String str = type +" "+(name == null ? n.getId() : name) +" [" +dist+" m ]";
Amenity n = closestAmenities.get(i);
int dist = (int) (MapUtils.getDistance(n.getNode(), newLatitude, newLongitude));
String str = n.getSimpleFormat() + " [" +dist+" m ]";
((DefaultMutableTreeNode)amenitiesTree.getChildAt(0)).add(
new DataExtractionTreeNode(str, n));
}
@ -492,10 +481,9 @@ public class DataExtraction implements IMapLocationListener {
}
p.removeAllChildren();
for (Node n : filter.get(s)) {
String name = n.getTag(OSMTagKey.NAME);
int dist = (int) (MapUtils.getDistance(n, newLatitude, newLongitude));
String str = (name == null ? n.getId() : name) + " [" + dist + " m ]";
for (Amenity n : filter.get(s)) {
int dist = (int) (MapUtils.getDistance(n.getNode(), newLatitude, newLongitude));
String str = n.getSimpleFormat() + " [" + dist + " m ]";
DataExtractionTreeNode node = new DataExtractionTreeNode(str, n);
p.add(node);
}

View file

@ -238,7 +238,7 @@ public class MapPanel extends JPanel implements IMapDownloaderCallback {
if(loadIfNeeded && cache.get(file) == null){
String urlToLoad = map.getUrlToLoad(x, y, zoom);
if (urlToLoad != null) {
downloader.requestToDownload(urlToLoad, new DownloadRequest(en, x, y, zoom));
downloader.requestToDownload(new DownloadRequest(urlToLoad, en, x, y, zoom));
}
}
}
@ -248,7 +248,7 @@ public class MapPanel extends JPanel implements IMapDownloaderCallback {
}
@Override
public void tileDownloaded(String dowloadedUrl, DownloadRequest request) {
public void tileDownloaded(DownloadRequest request) {
int tileSize = getTileSize();
double xTileLeft = getXTile() - getSize().width / (2d * tileSize);
double yTileUp = getYTile() - getSize().height / (2d * tileSize);

View file

@ -44,7 +44,14 @@ public class MapTileDownloader {
*/
public interface IMapDownloaderCallback {
public void tileDownloaded(String dowloadedUrl, DownloadRequest fileSaved);
/**
* Sometimes null cold be passed as request
* That means that there were a lot of requests but
* once method is called
* (in order to not create a collection of request & reduce calling times)
* @param fileSaved
*/
public void tileDownloaded(DownloadRequest request);
}
/**
@ -55,15 +62,18 @@ public class MapTileDownloader {
public final int zoom;
public final int xTile;
public final int yTile;
public final String url;
public DownloadRequest(File fileToSave, int xTile, int yTile, int zoom) {
public DownloadRequest(String url, File fileToSave, int xTile, int yTile, int zoom) {
this.url = url;
this.fileToSave = fileToSave;
this.xTile = xTile;
this.yTile = yTile;
this.zoom = zoom;
}
public DownloadRequest(File fileToSave) {
public DownloadRequest(String url, File fileToSave) {
this.url = url;
this.fileToSave = fileToSave;
xTile = -1;
yTile = -1;
@ -100,68 +110,69 @@ public class MapTileDownloader {
}
}
public void requestToDownload(String url, DownloadRequest request){
public void requestToDownload(DownloadRequest request){
if(DefaultLauncherConstants.TILE_DOWNLOAD_MAX_ERRORS > 0 &&
currentErrors > DefaultLauncherConstants.TILE_DOWNLOAD_MAX_ERRORS){
return;
}
if(request.url == null){
return;
}
if (!isFileCurrentlyDownloaded(request.fileToSave)) {
threadPoolExecutor.execute(new DownloadMapWorker(url, request));
threadPoolExecutor.execute(new DownloadMapWorker(request));
}
}
private class DownloadMapWorker implements Runnable, Comparable<DownloadMapWorker> {
private long time = System.currentTimeMillis();
private final String downloadUrl;
private DownloadRequest request;
private DownloadMapWorker(String downloadUrl, DownloadRequest request){
this.downloadUrl = downloadUrl;
private DownloadMapWorker(DownloadRequest request){
this.request = request;
}
@Override
public void run() {
try {
if(log.isDebugEnabled()){
log.debug("Start downloading tile : " + downloadUrl);
log.debug("Start downloading tile : " + request.url);
}
URL url = new URL(downloadUrl);
URLConnection connection = url.openConnection();
connection.setRequestProperty("User-Agent", DefaultLauncherConstants.APP_NAME+"/"+DefaultLauncherConstants.APP_VERSION);
BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream(), 8 * 1024);
try {
if (request != null && request.fileToSave != null) {
request.fileToSave.getParentFile().mkdirs();
FileOutputStream stream = new FileOutputStream(request.fileToSave);
if (request != null && request.fileToSave != null && request.url != null) {
currentlyDownloaded.add(request.fileToSave);
try {
request.fileToSave.getParentFile().mkdirs();
URL url = new URL(request.url);
URLConnection connection = url.openConnection();
connection.setRequestProperty("User-Agent", DefaultLauncherConstants.APP_NAME + "/"
+ DefaultLauncherConstants.APP_VERSION);
BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream(), 8 * 1024);
FileOutputStream stream = null;
try {
stream = new FileOutputStream(request.fileToSave);
Algoritms.streamCopy(inputStream, stream);
stream.flush();
} finally {
currentlyDownloaded.remove(request.fileToSave);
Algoritms.closeStream(inputStream);
Algoritms.closeStream(stream);
}
}
} finally {
Algoritms.closeStream(inputStream);
}
if(log.isDebugEnabled()){
log.debug("Downloading tile : " + downloadUrl + " successfull " + (System.currentTimeMillis() - time) + " ms");
}
if(callback != null){
callback.tileDownloaded(downloadUrl, request);
if (log.isDebugEnabled()) {
log.debug("Downloading tile : " + request.url + " successfull " + (System.currentTimeMillis() - time) + " ms");
}
} catch (UnknownHostException e) {
currentErrors++;
log.error("UnknownHostException, cannot download tile " + downloadUrl, e);
log.error("UnknownHostException, cannot download tile " + request.url, e);
} catch (IOException e) {
currentErrors++;
log.warn("Cannot download tile : " + downloadUrl, e);
log.warn("Cannot download tile : " + request.url, e);
} finally {
currentlyDownloaded.remove(request.fileToSave);
}
if (callback != null) {
callback.tileDownloaded(request);
}
}
}
@Override

View file

@ -6,8 +6,6 @@ public class OsmandSettings {
public static boolean useInternetToDownloadTiles = DefaultLauncherConstants.loadMissingImages;
public static boolean showGPSLocationOnMap = DefaultLauncherConstants.showGPSCoordinates;
public static ITileSource tileSource = DefaultLauncherConstants.MAP_defaultTileSource;
public static boolean showPoiOverMap = true;

View file

@ -9,18 +9,13 @@ 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;
// First of all switch off gps listener should be implemented
public int IMPLEMENT_ON_STOP_RESUME_ACTIVITY = 3;
// OsmandMapTileView.java have problem with class loading (LogFactory, MapTileDownloader) -
// it is not editable in editor
// 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
@ -29,12 +24,11 @@ public class ToDoConstants {
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
* Write activity to show something about authors / donation ....
*/
public int INTRODUCE_RESOURCE_MANAGER = 7;
public int DESCRIBE_ABOUT_AUTHORS = 8;
}

View file

@ -0,0 +1,140 @@
package com.osmand.data;
import java.util.LinkedHashMap;
import java.util.Map;
import com.osmand.osm.Node;
import com.osmand.osm.OSMSettings.OSMTagKey;
public class Amenity {
// http://wiki.openstreetmap.org/wiki/Amenity
public enum AmenityType {
SUSTENANCE, // restaurant, cafe ...
EDUCATION, // school, ...
TRANSPORTATION, // car_wash, parking, ...
FINANCE, // bank, atm, ...
HEALTHCARE, // hospital ...
ENTERTAINMENT, // cinema, ... (+! sauna, brothel)
TOURISM, // hotel, sights, museum ..
SHOP, // convenience (product), clothes...
LEISURE, // sport
OTHER, // grave-yard, police, post-office
}
private static Map<String, AmenityType> prebuiltMap = new LinkedHashMap<String, AmenityType>();
static {
prebuiltMap.put("restaurant", AmenityType.SUSTENANCE);
prebuiltMap.put("food_court", AmenityType.SUSTENANCE);
prebuiltMap.put("fast_food", AmenityType.SUSTENANCE);
prebuiltMap.put("drinking_water", AmenityType.SUSTENANCE);
prebuiltMap.put("bbq", AmenityType.SUSTENANCE);
prebuiltMap.put("pub", AmenityType.SUSTENANCE);
prebuiltMap.put("bar", AmenityType.SUSTENANCE);
prebuiltMap.put("cafe", AmenityType.SUSTENANCE);
prebuiltMap.put("biergarten", AmenityType.SUSTENANCE);
prebuiltMap.put("kindergarten", AmenityType.EDUCATION);
prebuiltMap.put("school", AmenityType.EDUCATION);
prebuiltMap.put("college", AmenityType.EDUCATION);
prebuiltMap.put("library", AmenityType.EDUCATION);
prebuiltMap.put("university", AmenityType.EDUCATION);
prebuiltMap.put("ferry_terminal", AmenityType.TRANSPORTATION);
prebuiltMap.put("bicycle_parking", AmenityType.TRANSPORTATION);
prebuiltMap.put("bicycle_rental", AmenityType.TRANSPORTATION);
prebuiltMap.put("bus_station", AmenityType.TRANSPORTATION);
prebuiltMap.put("car_rental", AmenityType.TRANSPORTATION);
prebuiltMap.put("car_sharing", AmenityType.TRANSPORTATION);
prebuiltMap.put("car_wash", AmenityType.TRANSPORTATION);
prebuiltMap.put("grit_bin", AmenityType.TRANSPORTATION);
prebuiltMap.put("parking", AmenityType.TRANSPORTATION);
prebuiltMap.put("taxi", AmenityType.TRANSPORTATION);
prebuiltMap.put("atm", AmenityType.FINANCE);
prebuiltMap.put("bank", AmenityType.FINANCE);
prebuiltMap.put("bureau_de_change", AmenityType.FINANCE);
prebuiltMap.put("pharmacy", AmenityType.HEALTHCARE);
prebuiltMap.put("hospital", AmenityType.HEALTHCARE);
prebuiltMap.put("baby_hatch", AmenityType.HEALTHCARE);
prebuiltMap.put("dentist", AmenityType.HEALTHCARE);
prebuiltMap.put("doctors", AmenityType.HEALTHCARE);
prebuiltMap.put("veterinary", AmenityType.HEALTHCARE);
prebuiltMap.put("architect_office", AmenityType.ENTERTAINMENT);
prebuiltMap.put("arts_centre", AmenityType.ENTERTAINMENT);
prebuiltMap.put("cinema", AmenityType.ENTERTAINMENT);
prebuiltMap.put("community_centre", AmenityType.ENTERTAINMENT);
prebuiltMap.put("fountain", AmenityType.ENTERTAINMENT);
prebuiltMap.put("nightclub", AmenityType.ENTERTAINMENT);
prebuiltMap.put("stripclub", AmenityType.ENTERTAINMENT);
prebuiltMap.put("studio", AmenityType.ENTERTAINMENT);
prebuiltMap.put("theatre", AmenityType.ENTERTAINMENT);
prebuiltMap.put("sauna", AmenityType.ENTERTAINMENT);
prebuiltMap.put("brothel", AmenityType.ENTERTAINMENT);
}
private final Node node;
public Amenity(Node node){
this.node = node;
}
public Node getNode() {
return node;
}
public String getSubType(){
if(node.getTag(OSMTagKey.AMENITY) != null){
return node.getTag(OSMTagKey.AMENITY);
} else if(node.getTag(OSMTagKey.SHOP) != null){
return node.getTag(OSMTagKey.SHOP);
} else if(node.getTag(OSMTagKey.TOURISM) != null){
return node.getTag(OSMTagKey.TOURISM);
} else if(node.getTag(OSMTagKey.LEISURE) != null){
return node.getTag(OSMTagKey.LEISURE);
}
return "";
}
public AmenityType getType(){
if(node.getTag(OSMTagKey.SHOP) != null){
return AmenityType.SHOP;
} else if(node.getTag(OSMTagKey.TOURISM) != null){
return AmenityType.TOURISM;
} else if(node.getTag(OSMTagKey.LEISURE) != null){
return AmenityType.LEISURE;
} else if(prebuiltMap.containsKey(node.getTag(OSMTagKey.AMENITY))){
return prebuiltMap.get(node.getTag(OSMTagKey.AMENITY));
}
return AmenityType.OTHER;
}
public static boolean isAmenity(Node n){
if(n.getTag(OSMTagKey.AMENITY) != null){
return true;
} else if(n.getTag(OSMTagKey.SHOP) != null){
return true;
} else if(n.getTag(OSMTagKey.LEISURE) != null){
return true;
} else if(n.getTag(OSMTagKey.TOURISM) != null){
return true;
}
return false;
}
public String getSimpleFormat(){
String name = node.getTag(OSMTagKey.NAME);
return getType().toString() +" : " + getSubType() + " " +(name == null ? node.getId() : name);
}
@Override
public String toString() {
return getSimpleFormat();
}
}

View file

@ -17,7 +17,7 @@ import com.osmand.osm.OSMSettings.OSMTagKey;
public class Region {
private Entity entity;
private DataTileManager<Node> amenities = new DataTileManager<Node>();
private DataTileManager<Amenity> amenities = new DataTileManager<Amenity>();
private Map<CityType, Collection<City>> cities = new HashMap<CityType, Collection<City>>();
{
@ -87,12 +87,12 @@ public class Region {
return closest;
}
public List<Node> getClosestAmenities(double latitude, double longitude){
public List<Amenity> getClosestAmenities(double latitude, double longitude){
return amenities.getClosestObjects(latitude, longitude, 2);
}
public void registerAmenity(Node n){
amenities.registerObject(n.getLatitude(), n.getLongitude(), n);
public void registerAmenity(Amenity a){
amenities.registerObject(a.getNode().getLatitude(), a.getNode().getLongitude(), a);
}

View file

@ -5,7 +5,7 @@
android:layout_height="fill_parent">
<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="bottom|left" android:id="@+id/BackToMenu" android:background="@drawable/back"></ImageButton>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<group android:id="@+id/map_context_menu" android:menuCategory="container"><item android:id="@+id/map_show_settings" android:title="@string/settings_Button"></item>
<item android:id="@+id/map_show_location" android:title="@string/show_location"></item>
</group>
</menu>

View file

@ -2,7 +2,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<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>

View file

@ -85,7 +85,9 @@ public class LogUtil {
@Override
public boolean isDebugEnabled() {
return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
// For debur purposes always true
// return android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
return true;
}
@Override

View file

@ -0,0 +1,260 @@
package com.osmand;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.tools.bzip2.CBZip2InputStream;
import org.xml.sax.SAXException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import com.osmand.MapTileDownloader.DownloadRequest;
import com.osmand.data.Amenity;
import com.osmand.data.DataTileManager;
import com.osmand.map.ITileSource;
import com.osmand.osm.Entity;
import com.osmand.osm.Node;
import com.osmand.osm.io.OsmBaseStorage;
/**
* Resource manager is responsible to work with all resources
* that could consume memory (especially with file resources).
* Such as indexes, tiles.
* Also it is responsible to create cache for that resources if they
* can't be loaded fully into memory & clear them on request.
*
*/
public class ResourceManager {
private static final String POI_PATH = "osmand/poi/";
private static final String TILES_PATH = "osmand/tiles/";
private static final Log log = LogUtil.getLog(ResourceManager.class);
protected static ResourceManager manager = null;
public static ResourceManager getResourceManager(){
if(manager == null){
manager = new ResourceManager();
}
return manager;
}
// it is not good investigated but no more than 64 (satellite images)
protected final int maxImgCacheSize = 64;
private DataTileManager<Amenity> poiIndex = null;
protected Map<String, Bitmap> cacheOfImages = new LinkedHashMap<String, Bitmap>();
protected File dirWithTiles ;
private MapTileDownloader downloader = MapTileDownloader.getInstance();
public AsyncLoadingThread asyncLoadingTiles = new AsyncLoadingThread();
public ResourceManager() {
// TODO start/stop this thread when needed?
asyncLoadingTiles.start();
dirWithTiles = new File(Environment.getExternalStorageDirectory(), TILES_PATH);
}
/// Working with tiles ///
public Bitmap getTileImageForMapAsync(ITileSource map, int x, int y, int zoom, boolean loadFromInternetIfNeeded) {
return getTileImageForMap(map, x, y, zoom, loadFromInternetIfNeeded, false);
}
public Bitmap getTileImageForMapSync(ITileSource map, int x, int y, int zoom, boolean loadFromInternetIfNeeded) {
return getTileImageForMap(map, x, y, zoom, loadFromInternetIfNeeded, true);
}
protected Bitmap getTileImageForMap(ITileSource map, int x, int y, int zoom,
boolean loadFromInternetIfNeeded, boolean sync) {
if (map == null) {
return null;
}
StringBuilder builder = new StringBuilder(40);
builder.append(map.getName()).append('/').append(zoom). append('/').append(x).
append('/').append(y).append(map.getTileFormat()).append(".tile");
String file = builder.toString();
if (cacheOfImages.get(file) == null) {
String url = loadFromInternetIfNeeded ? map.getUrlToLoad(x, y, zoom) : null;
TileLoadDownloadRequest req = new TileLoadDownloadRequest(dirWithTiles, file, url, new File(dirWithTiles, file),
x, y, zoom);
if(sync){
return getRequestedImageTile(req);
} else {
asyncLoadingTiles.requestToLoadImage(req);
}
}
return cacheOfImages.get(file);
}
private static class TileLoadDownloadRequest extends DownloadRequest {
public final String fileToLoad;
public final File dirWithTiles;
public TileLoadDownloadRequest(File dirWithTiles,
String fileToLoad, String url, File fileToSave, int tileX, int tileY, int zoom) {
super(url, fileToSave, tileX, tileY, zoom);
this.dirWithTiles = dirWithTiles;
this.fileToLoad = fileToLoad;
}
}
public class AsyncLoadingThread extends Thread {
Stack<TileLoadDownloadRequest> requests = new Stack<TileLoadDownloadRequest>();
public AsyncLoadingThread(){
super("Async loading tiles");
}
@Override
public void run() {
while(true){
try {
boolean update = false;
while(!requests.isEmpty()){
TileLoadDownloadRequest r = requests.pop();
if(cacheOfImages.get(r.fileToLoad) == null) {
update |= getRequestedImageTile(r) != null;
}
}
if(update){
// use downloader callback
downloader.getDownloaderCallback().tileDownloaded(null);
}
sleep(750);
} catch (InterruptedException e) {
log.error(e);
} catch (RuntimeException e){
log.error(e);
}
}
}
public void requestToLoadImage(TileLoadDownloadRequest req){
requests.push(req);
}
};
private Bitmap getRequestedImageTile(TileLoadDownloadRequest req){
if(req.fileToLoad == null || req.dirWithTiles == null){
return null;
}
File en = new File(req.dirWithTiles, req.fileToLoad);
if (cacheOfImages.size() > maxImgCacheSize) {
onLowMemory();
}
if (!downloader.isFileCurrentlyDownloaded(en)) {
if (en.exists()) {
long time = System.currentTimeMillis();
cacheOfImages.put(req.fileToLoad, BitmapFactory.decodeFile(en.getAbsolutePath()));
if (log.isDebugEnabled()) {
log.debug("Loaded file : " + req.fileToLoad + " " + -(time - System.currentTimeMillis()) + " ms");
}
}
if(cacheOfImages.get(req.fileToLoad) == null && req.url != null){
// TODO we could check that network is available (context is required)
// ConnectivityManager mgr = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
// NetworkInfo info = mgr.getActiveNetworkInfo();
// if (info != null && info.isConnected()) {
// downloader.requestToDownload(req);
// }
downloader.requestToDownload(req);
}
}
return cacheOfImages.get(req.fileToLoad);
}
// POI INDEX //
public void indexingPoi(){
if (poiIndex == null) {
File file = new File(Environment.getExternalStorageDirectory(), POI_PATH);
poiIndex = new DataTileManager<Amenity>();
if (file.exists() && file.canRead()) {
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 && Amenity.isAmenity((Node) e)) {
poiIndex.registerObject(((Node)e).getLatitude(), ((Node)e).getLongitude(), new Amenity((Node) e));
}
}
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);
}
}
}
}
}
}
public DataTileManager<Amenity> getPoiIndex() {
if(poiIndex == null){
indexingPoi();
}
return poiIndex;
}
/// On low memory method ///
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();
}
}

View file

@ -9,6 +9,7 @@ import android.view.View.OnClickListener;
import android.widget.Button;
import com.osmand.R;
import com.osmand.ResourceManager;
public class MainMenuActivity extends Activity {
@ -47,5 +48,7 @@ public class MainMenuActivity extends Activity {
MainMenuActivity.this.finish();
}
});
ResourceManager.getResourceManager().indexingPoi();
}
}

View file

@ -1,13 +1,6 @@
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 java.text.MessageFormat;
import android.app.Activity;
import android.content.Intent;
@ -15,26 +8,21 @@ import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Toast;
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.ResourceManager;
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;
@ -53,12 +41,6 @@ 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
@ -72,22 +54,23 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
setContentView(R.layout.main);
mapView = (OsmandMapTileView) findViewById(R.id.MapView);
mapView.setFileWithTiles(new File(Environment.getExternalStorageDirectory(), TILES_PATH));
mapView.addMapLocationListener(this);
mapView.setMapLocationListener(this);
poiMapLayer = new POIMapLayer();
poiMapLayer.setNodeManager(ResourceManager.getResourceManager().getPoiIndex());
mapView.addLayer(poiMapLayer);
ZoomControls zoomControls = (ZoomControls) findViewById(R.id.ZoomControls01);
zoomControls.setOnZoomInClickListener(new OnClickListener() {
@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());
}
});
@ -109,6 +92,7 @@ public class MapActivity extends Activity implements LocationListener, IMapLocat
});
backToMenu = (ImageButton)findViewById(R.id.BackToMenu);
backToMenu.setOnClickListener(new OnClickListener(){
@Override
@ -122,61 +106,9 @@ 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;
}
@ -251,32 +183,54 @@ 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(mapView.getLayers().contains(poiMapLayer) != OsmandSettings.showPoiOverMap){
if(OsmandSettings.showPoiOverMap){
poiMapLayer.setVisibility(View.VISIBLE);
mapView.addLayer(poiMapLayer);
} else {
poiMapLayer.setVisibility(View.INVISIBLE);
mapView.removeLayer(poiMapLayer);
}
}
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
ResourceManager.getResourceManager().onLowMemory();
}
@Override
public void locationChanged(double newLatitude, double newLongitude, Object source) {
// when user start dragging
if(source == mapView && lastKnownLocation != null){
if(lastKnownLocation != null){
linkLocationWithMap = false;
backToLocation.setVisibility(View.VISIBLE);
}
poiMapLayer.setCurrentLocationAndZoom(new LatLon(newLatitude, newLongitude), mapView.getZoom());
validatePointOfView();
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.map_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.map_show_location){
float f= (Runtime.getRuntime().totalMemory())/ 1e6f;
String text = MessageFormat.format("Latitude : {0}, longitude : {1}, zoom : {2}, memory : {3}", mapView.getLatitude(),
mapView.getLongitude(), mapView.getZoom(), f);
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
return true;
} else if (item.getItemId() == R.id.map_show_settings) {
final Intent settings = new Intent(MapActivity.this, SettingsActivity.class);
startActivity(settings);
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -17,11 +17,9 @@ 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;
@ -31,8 +29,6 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_pref);
PreferenceScreen screen = getPreferenceScreen();
showGpsLocation =(CheckBoxPreference) screen.findPreference(show_gps_location_text);
showGpsLocation.setOnPreferenceChangeListener(this);
useInternetToDownloadTiles =(CheckBoxPreference) screen.findPreference(use_internet_to_download_tiles);
useInternetToDownloadTiles.setOnPreferenceChangeListener(this);
showPoiOnMap =(CheckBoxPreference) screen.findPreference(show_poi_over_map);
@ -48,7 +44,6 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
protected void onResume() {
super.onResume();
useInternetToDownloadTiles.setChecked(OsmandSettings.useInternetToDownloadTiles);
showGpsLocation.setChecked(OsmandSettings.showGPSLocationOnMap);
showPoiOnMap.setChecked(OsmandSettings.showPoiOverMap);
List<TileSourceTemplate> list = TileSourceManager.getKnownSourceTemplates();
@ -59,15 +54,14 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
tileSourcePreference.setEntries(entries);
tileSourcePreference.setEntryValues(entries);
tileSourcePreference.setValue(OsmandSettings.tileSource.getName());
tileSourcePreference.setSummary(tileSourcePreference.getSummary() + "\t\t[" + OsmandSettings.tileSource.getName()+"]");
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if(preference == showGpsLocation){
OsmandSettings.showGPSLocationOnMap = (Boolean) newValue;
} else if(preference == showPoiOnMap){
if(preference == showPoiOnMap){
OsmandSettings.showPoiOverMap = (Boolean) newValue;
} else if(preference == useInternetToDownloadTiles){
OsmandSettings.useInternetToDownloadTiles = (Boolean) newValue;

View file

@ -0,0 +1,92 @@
package com.osmand.views;
/**
* Thread for animated dragging.
* Defines accelerator to stop dragging screen.
*/
public class AnimateDraggingMapThread implements Runnable {
public interface AnimateDraggingCallback {
public void dragTo(float curX, float curY, float newX, float newY);
}
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;
private volatile boolean stopped;
private final float a = 0.001f;
private volatile Thread currentThread = null;
private AnimateDraggingCallback callback = null;
@Override
public void run() {
currentThread = Thread.currentThread();
try {
while (!stopped && (vx > 0 || vy > 0)) {
Thread.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 (!stopped && callback != null) {
callback.dragTo(curX, curY, newX, newY);
}
vx -= ax * dt;
vy -= ay * dt;
time = curT;
curX = newX;
curY = newY;
}
} catch (InterruptedException e) {
}
currentThread = null;
}
/**
* Stop dragging async
*/
public void stopDragging(){
stopped = true;
}
/**
* Stop dragging sync
*/
public void stopDraggingSync(){
// wait until current thread != null
// TODO implement better method for waintg
stopped = true;
while(currentThread != null){}
}
public void startDragging(float dTime, float startX, float startY, float endX, float endY){
stopDraggingSync();
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;
time = System.currentTimeMillis();
stopped = false;
Thread thread = new Thread(this,"Animatable dragging");
thread.start();
}
public AnimateDraggingCallback getCallback() {
return callback;
}
public void setCallback(AnimateDraggingCallback callback) {
this.callback = callback;
}
}

View file

@ -0,0 +1,17 @@
package com.osmand.views;
import android.graphics.Canvas;
import android.view.MotionEvent;
public interface OsmandMapLayer {
public void initLayer(OsmandMapTileView view);
public void onDraw(Canvas canvas);
public void destroyLayer();
public boolean onTouchEvent(MotionEvent event);
}

View file

@ -1,30 +1,19 @@
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 org.apache.commons.logging.Log;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
@ -36,28 +25,21 @@ import com.osmand.IMapLocationListener;
import com.osmand.LogUtil;
import com.osmand.MapTileDownloader;
import com.osmand.OsmandSettings;
import com.osmand.ResourceManager;
import com.osmand.MapTileDownloader.DownloadRequest;
import com.osmand.MapTileDownloader.IMapDownloaderCallback;
import com.osmand.map.ITileSource;
import com.osmand.osm.MapUtils;
import com.osmand.views.AnimateDraggingMapThread.AnimateDraggingCallback;
public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, Callback{
public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCallback, Callback, AnimateDraggingCallback{
protected final int emptyTileDivisor = DefaultLauncherConstants.MAP_divNonLoadedImage;
protected final int maxImgCacheSize = 96;
protected int drawCoordinatesX = 0;
protected int drawCoordinatesY = 55;
protected final int timeForDraggingAnimation = 300;
protected final int minimumDistanceForDraggingAnimation = 40;
protected static final Log log = LogUtil.getLog(OsmandMapTileView.class);
/**
* file or directory with tiles
*/
private File fileWithTiles;
/**
* zoom level
*/
@ -70,24 +52,24 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
// name of source map
private ITileSource map = null;
/**
* listeners
*/
private List<IMapLocationListener> listeners = new ArrayList<IMapLocationListener>();
private IMapLocationListener locationListener;
private MapTileDownloader downloader = MapTileDownloader.getInstance();
Map<String, Bitmap> cacheOfImages = new HashMap<String, Bitmap>();
private List<OsmandMapLayer> layers = new ArrayList<OsmandMapLayer>();
private boolean isStartedDragging = false;
private double startDraggingX = 0d;
private double startDraggingY = 0d;
private PointF initStartDragging = null;
// UI Part
private AnimateDraggingMapThread animatedDraggingThread;
private PointF startDragging = null;
private PointF autoStartDragging = null;
private long autoStartDraggingTime = 0;
Paint paintGrayFill;
Paint paintWhiteFill;
Paint paintBlack;
private AnimatedDragging animatedDraggingThread;
@ -101,6 +83,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
initView();
}
/////////////////////////////// INITIALIZING UI PART ///////////////////////////////////
public void initView(){
paintGrayFill = new Paint();
paintGrayFill.setColor(Color.GRAY);
@ -117,117 +100,46 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
setClickable(true);
getHolder().addCallback(this);
downloader.setDownloaderCallback(this);
asyncLoadingTiles.start();
animatedDraggingThread = new AnimateDraggingMapThread();
animatedDraggingThread.setCallback(this);
}
@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 ?
}
public void addLayer(OsmandMapLayer layer){
layers.add(layer);
layer.initLayer(this);
}
public void removeLayer(OsmandMapLayer layer){
layers.remove(layer);
layer.destroyLayer();
}
public List<OsmandMapLayer> getLayers() {
return layers;
}
/////////////////////////// NON UI PART (could be extracted in common) /////////////////////////////
public int getTileSize() {
return map == null ? 256 : map.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();
// 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(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(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(isStartedDragging){
dragTo(startDraggingX, startDraggingY, event.getX(), event.getY());
startDraggingX = event.getX();
startDraggingY = event.getY();
}
}
return super.onTouchEvent(event);
}
public double getXTile(){
return MapUtils.getTileNumberX(zoom, longitude);
}
@ -236,6 +148,63 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
return MapUtils.getTileNumberY(zoom, latitude);
}
public void setZoom(int zoom){
if (map == null || (map.getMaximumZoomSupported() >= zoom && map.getMinimumZoomSupported() <= zoom)) {
animatedDraggingThread.stopDragging();
this.zoom = zoom;
prepareImage();
}
}
public ITileSource getMap() {
return map;
}
public void setMap(ITileSource map) {
this.map = map;
if(map.getMaximumZoomSupported() < this.zoom){
zoom = map.getMaximumZoomSupported();
}
if(map.getMinimumZoomSupported() > this.zoom){
zoom = map.getMinimumZoomSupported();
}
prepareImage();
}
public void setLatLon(double latitude, double longitude){
animatedDraggingThread.stopDragging();
this.latitude = latitude;
this.longitude = longitude;
prepareImage();
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public int getZoom() {
return zoom;
}
public void setMapLocationListener(IMapLocationListener l){
locationListener = l;
}
/**
* Adds listener to control when map is dragging
*/
public IMapLocationListener setMapLocationListener(){
return locationListener;
}
//////////////////////////////// DRAWING MAP PART /////////////////////////////////////////////
protected void drawEmptyTile(Canvas cvs, int x, int y){
int tileDiv = getTileSize() / emptyTileDivisor;
for (int k1 = 0; k1 < emptyTileDivisor; k1++) {
@ -252,144 +221,12 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
prepareImage();
super.onSizeChanged(w, h, oldw, oldh);
}
public String getFileForImage (int x, int y, int zoom, String ext){
return map.getName() +"/"+zoom+"/"+(x) +"/"+y+ext+".tile";
}
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(fileWithTiles == null || !fileWithTiles.canRead()){
return null;
}
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");
}
}
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));
}
}
}
}
return cacheOfImages.get(file);
}
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 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);
for(OsmandMapLayer layer : layers){
layer.onDraw(canvas);
}
}
@ -413,7 +250,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
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);
ResourceManager mgr = ResourceManager.getResourceManager();
Bitmap bmp = mgr.getTileImageForMapAsync(map, xTileLeft + i, yTileUp + j, zoom, OsmandSettings.useInternetToDownloadTiles);
if (bmp == null) {
drawEmptyTile(canvas, i * tileSize + startingX, j * tileSize + startingY);
} else {
@ -430,104 +268,119 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall
}
public void setZoom(int zoom){
if (map == null || (map.getMaximumZoomSupported() >= zoom && map.getMinimumZoomSupported() <= zoom)) {
if(animatedDraggingThread != null){
animatedDraggingThread.stopEvaluation();
}
this.zoom = zoom;
public void tileDownloaded(DownloadRequest request) {
if(request == null){
// we don't know exact images were changed
prepareImage();
return;
}
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 {
ResourceManager mgr = ResourceManager.getResourceManager();
Bitmap bmp = mgr.getTileImageForMapSync(map, request.xTile, request.yTile, zoom, false);
if (bmp == null) {
drawEmptyTile(canvas, i, j);
} else {
canvas.drawBitmap(bmp, i, j, null);
}
drawOverMap(canvas);
} finally {
holder.unlockCanvasAndPost(canvas);
}
}
}
}
}
public File getFileWithTiles() {
return fileWithTiles;
}
public ITileSource getMap() {
return map;
}
public void setMap(ITileSource map) {
this.map = map;
if(map.getMaximumZoomSupported() < this.zoom){
zoom = map.getMaximumZoomSupported();
}
if(map.getMinimumZoomSupported() > this.zoom){
zoom = map.getMinimumZoomSupported();
}
prepareImage();
}
public void setFileWithTiles(File fileWithTiles) {
this.fileWithTiles = fileWithTiles;
prepareImage();
}
public void setLatLon(double latitude, double longitude){
this.latitude = latitude;
this.longitude = longitude;
prepareImage();
fireMapLocationListeners(null);
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public int getZoom() {
return zoom;
}
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();
}
/////////////////////////////////// DRAGGING PART ///////////////////////////////////////
public void addMapLocationListener(IMapLocationListener l){
listeners.add(l);
}
public void removeMapLocationListener(IMapLocationListener l){
listeners.remove(l);
}
protected void fireMapLocationListeners(Object source){
for(IMapLocationListener l : listeners){
l.locationChanged(latitude, longitude, source);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
public void dragTo(float fromX, float fromY, float toX, float toY){
float dx = (fromX - toX)/getTileSize();
float dy = (fromY - toY)/getTileSize();
this.latitude = MapUtils.getLatitudeFromTile(zoom, getYTile() + dy);
this.longitude = MapUtils.getLongitudeFromTile(zoom, getXTile() + dx);
prepareImage();
if(locationListener != null){
locationListener.locationChanged(latitude, longitude, this);
}
}
public boolean wasMapDraggingAccelerated(MotionEvent event){
if(autoStartDragging == null){
return false;
}
if(event.getEventTime() - autoStartDraggingTime < timeForDraggingAnimation){
float dist = Math.abs(event.getX() - autoStartDragging.x) + Math.abs(event.getY() - autoStartDragging.y);
if(dist > minimumDistanceForDraggingAnimation){
return true;
}
}
return false;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
prepareImage();
public boolean onTouchEvent(MotionEvent event) {
for (int i = layers.size() - 1; i >= 0; i--) {
if (layers.get(i).onTouchEvent(event)) {
return true;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO clear cache ?
if(event.getAction() == MotionEvent.ACTION_DOWN) {
animatedDraggingThread.stopDragging();
if(startDragging == null){
autoStartDragging = new PointF(event.getX(), event.getY());
autoStartDraggingTime = event.getEventTime();
startDragging = new PointF(event.getX(), event.getY());
}
} else if(event.getAction() == MotionEvent.ACTION_UP) {
if(startDragging != null){
dragTo(startDragging.x, startDragging.y, event.getX(), event.getY());
startDragging = null;
if(wasMapDraggingAccelerated(event)){
float timeDist = (int) (event.getEventTime() - autoStartDraggingTime);
if(timeDist < 20){
timeDist = 20;
}
animatedDraggingThread.startDragging(timeDist, autoStartDragging.x, autoStartDragging.y,
event.getX(), event.getY());
}
}
} else if(event.getAction() == MotionEvent.ACTION_MOVE) {
if(startDragging != null){
dragTo(startDragging.x, startDragging.y, event.getX(), event.getY());
// save memory do not create new PointF
startDragging.x = event.getX();
startDragging.y = event.getY();
if(event.getEventTime() - autoStartDraggingTime > timeForDraggingAnimation){
autoStartDraggingTime = event.getEventTime();
autoStartDragging.x = event.getX();
autoStartDragging.y = event.getY();
}
}
}
return super.onTouchEvent(event);
}
}

View file

@ -1,154 +1,111 @@
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.Amenity;
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>();
public class POIMapLayer implements OsmandMapLayer {
private static final int radiusClick = 2; // for 15 level zoom
private DataTileManager<Amenity> nodeManager = null;
private Paint pointUI;
private static final int radiusClick = 16;
private Toast previousShownToast =null;
private final static Log log = LogUtil.getLog(POIMapLayer.class);
private OsmandMapTileView view;
private List<Amenity> objects;
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();
}
// TODO optimize all evaluations
@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());
if(event.getAction() == MotionEvent.ACTION_DOWN && objects != null) {
double tileNumberX = MapUtils.getTileNumberX(view.getZoom(), view.getLongitude());
double tileNumberY = MapUtils.getTileNumberY(view.getZoom(), view.getLatitude());
double xTileLeft = tileNumberX - view.getWidth() / (2d * getTileSize());
double yTileUp = tileNumberY - view.getHeight() / (2d * getTileSize());
int ex = (int) event.getX();
int ey = (int) event.getY();
int radius = getRadiusPoi(view.getZoom()) * 3 / 2;
for(Amenity n : objects){
double tileX = MapUtils.getTileNumberX(view.getZoom(), n.getNode().getLongitude());
int x = (int) ((tileX - xTileLeft) * getTileSize());
double tileY = MapUtils.getTileNumberY(view.getZoom(), n.getNode().getLatitude());
int y = (int) ((tileY - yTileUp) * getTileSize());
if(Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius){
Toast.makeText(view.getContext(), n.getSimpleFormat(), Toast.LENGTH_SHORT).show();
return true;
}
}
}
return super.onTouchEvent(event);
}
public void setCurrentLocationAndZoom(LatLon currentLocation, int zoom) {
this.currentLocation = currentLocation;
this.zoomLevel = zoom;
preparePoints();
// return super.onTouchEvent(event);
return false;
}
public int getTileSize(){
ITileSource source = OsmandSettings.tileSource;
return source == null ? 256 : source.getTileSize();
return view.getTileSize();
}
public void setNodeManager(DataTileManager<Node> nodeManager) {
public void setNodeManager(DataTileManager<Amenity> nodeManager) {
this.nodeManager = nodeManager;
preparePoints();
if(view != null){
view.prepareImage();
}
}
public LatLon getCurrentLocation() {
return currentLocation;
}
public DataTileManager<Node> getNodeManager() {
public DataTileManager<Amenity> getNodeManager() {
return nodeManager;
}
@Override
public void initLayer(OsmandMapTileView view) {
this.view = view;
pointUI = new Paint();
pointUI.setColor(Color.BLUE);
pointUI.setAlpha(150);
pointUI.setAntiAlias(true);
}
public int getRadiusPoi(int zoom){
if(zoom < 15){
return 0;
} else {
return radiusClick << (zoom - 15);
}
}
@Override
public void onDraw(Canvas canvas) {
if (nodeManager != null && view.getZoom() >= 15) {
double tileNumberX = MapUtils.getTileNumberX(view.getZoom(), view.getLongitude());
double tileNumberY = MapUtils.getTileNumberY(view.getZoom(), view.getLatitude());
double xTileLeft = tileNumberX - view.getWidth() / (2d * getTileSize());
double xTileRight = tileNumberX + view.getWidth() / (2d * getTileSize());
double yTileUp = tileNumberY - view.getHeight() / (2d * getTileSize());
double yTileDown = tileNumberY + view.getHeight() / (2d * getTileSize());
objects = nodeManager.getObjects(MapUtils.getLatitudeFromTile(view.getZoom(), yTileUp), MapUtils
.getLongitudeFromTile(view.getZoom(), xTileLeft), MapUtils.getLatitudeFromTile(view.getZoom(), yTileDown), MapUtils
.getLongitudeFromTile(view.getZoom(), xTileRight));
for (Amenity o : objects) {
double tileX = MapUtils.getTileNumberX(view.getZoom(), o.getNode().getLongitude());
int x = (int) ((tileX - xTileLeft) * getTileSize());
double tileY = MapUtils.getTileNumberY(view.getZoom(), o.getNode().getLatitude());
int y = (int) ((tileY - yTileUp) * getTileSize());
canvas.drawCircle(x, y, getRadiusPoi(view.getZoom()), pointUI);
}
}
}
@Override
public void destroyLayer() {
public int getZoomLevel() {
return zoomLevel;
}
}