vtm-app: revive / update with latest VTM, closes #90
This commit is contained in:
1
vtm-app/src/android-logger.properties
Normal file
1
vtm-app/src/android-logger.properties
Normal file
@@ -0,0 +1 @@
|
||||
root=DEBUG:%logger
|
||||
70
vtm-app/src/org/oscim/app/App.java
Normal file
70
vtm-app/src/org/oscim/app/App.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import org.oscim.android.MapView;
|
||||
import org.oscim.map.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class App extends Application {
|
||||
|
||||
public final static Logger log = LoggerFactory.getLogger(App.class);
|
||||
|
||||
public static Map map;
|
||||
public static MapView view;
|
||||
public static Resources res;
|
||||
public static TileMap activity;
|
||||
|
||||
public static POISearch poiSearch;
|
||||
public static RouteSearch routeSearch;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
res = getResources();
|
||||
}
|
||||
|
||||
public static void lockOrientation(Activity activity) {
|
||||
Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
int rotation = display.getRotation();
|
||||
int tempOrientation = activity.getResources().getConfiguration().orientation;
|
||||
int orientation = 0;
|
||||
switch (tempOrientation) {
|
||||
case Configuration.ORIENTATION_LANDSCAPE:
|
||||
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
else
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
break;
|
||||
case Configuration.ORIENTATION_PORTRAIT:
|
||||
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270)
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
else
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
}
|
||||
activity.setRequestedOrientation(orientation);
|
||||
}
|
||||
}
|
||||
31
vtm-app/src/org/oscim/app/ConnectionHandler.java
Normal file
31
vtm-app/src/org/oscim/app/ConnectionHandler.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package org.oscim.app;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ConnectionHandler extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) context
|
||||
.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
|
||||
// NetworkInfo mobNetInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE );
|
||||
if (activeNetInfo != null) {
|
||||
if (activeNetInfo.isConnected()) {
|
||||
Toast.makeText(context, "Active Network Type : " + activeNetInfo.getTypeName(),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
//if (App.map != null)
|
||||
// App.map.redrawMap();
|
||||
}
|
||||
//Toast.makeText( context, "Active Network Type : " + activeNetInfo.getTypeName(), Toast.LENGTH_SHORT ).show();
|
||||
}
|
||||
// if( mobNetInfo != null )
|
||||
// {
|
||||
// Toast.makeText( context, "Mobile Network Type : " + mobNetInfo.getTypeName(), Toast.LENGTH_SHORT ).show();
|
||||
// }
|
||||
}
|
||||
}
|
||||
63
vtm-app/src/org/oscim/app/FileUtils.java
Normal file
63
vtm-app/src/org/oscim/app/FileUtils.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import android.content.res.Resources;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
final class FileUtils {
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.00 ");
|
||||
private static final double ONE_GIGABYTE = 1000000000;
|
||||
private static final double ONE_KILOBYTE = 1000;
|
||||
private static final double ONE_MEGABYTE = 1000000;
|
||||
|
||||
/**
|
||||
* Formats the given file size as a human readable string, using SI
|
||||
* prefixes.
|
||||
*
|
||||
* @param fileSize the file size to be formatted.
|
||||
* @param resources a reference to the application resources.
|
||||
* @return a human readable file size.
|
||||
* @throws IllegalArgumentException if the given file size is negative.
|
||||
*/
|
||||
static String formatFileSize(long fileSize, Resources resources) {
|
||||
if (fileSize < 0) {
|
||||
throw new IllegalArgumentException("invalid file size: " + fileSize);
|
||||
} else if (fileSize < 1000) {
|
||||
if (fileSize == 1) {
|
||||
// singular
|
||||
return "1 " + resources.getString(R.string.file_size_byte);
|
||||
}
|
||||
|
||||
// plural, including zero
|
||||
return fileSize + " " + resources.getString(R.string.file_size_bytes);
|
||||
} else {
|
||||
if (fileSize < ONE_MEGABYTE) {
|
||||
return DECIMAL_FORMAT.format(fileSize / ONE_KILOBYTE)
|
||||
+ resources.getString(R.string.file_size_kb);
|
||||
} else if (fileSize < ONE_GIGABYTE) {
|
||||
return DECIMAL_FORMAT.format(fileSize / ONE_MEGABYTE)
|
||||
+ resources.getString(R.string.file_size_mb);
|
||||
}
|
||||
return DECIMAL_FORMAT.format(fileSize / ONE_GIGABYTE)
|
||||
+ resources.getString(R.string.file_size_gb);
|
||||
}
|
||||
}
|
||||
|
||||
private FileUtils() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
47
vtm-app/src/org/oscim/app/InfoView.java
Normal file
47
vtm-app/src/org/oscim/app/InfoView.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* Simple activity to display the info web page from the assets folder.
|
||||
*/
|
||||
public class InfoView extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
WebView webView = new WebView(this);
|
||||
webView.loadUrl("file:///android_asset/info.xml");
|
||||
setContentView(webView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// check if the full screen mode should be activated
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen", false)) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
125
vtm-app/src/org/oscim/app/MapActivity.java
Normal file
125
vtm-app/src/org/oscim/app/MapActivity.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
import org.oscim.android.MapView;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.map.Map;
|
||||
|
||||
/**
|
||||
* MapActivity is the abstract base class which can be extended in order to use
|
||||
* a {@link MapView}. There are no abstract methods in this implementation which
|
||||
* subclasses need to override and no API key or registration is required.
|
||||
* <p/>
|
||||
* A subclass may create a MapView either via one of the MapView constructors or
|
||||
* by inflating an XML layout file.
|
||||
* <p/>
|
||||
* When the MapActivity is shut down, the current center position, zoom level
|
||||
* and map file of the MapView are saved in a preferences file and restored in
|
||||
* the next startup process.
|
||||
*/
|
||||
public abstract class MapActivity extends Activity {
|
||||
private static final String KEY_LATITUDE = "latitude";
|
||||
private static final String KEY_LONGITUDE = "longitude";
|
||||
private static final String KEY_MAP_SCALE = "map_scale";
|
||||
|
||||
private static final String PREFERENCES_FILE = "MapActivity";
|
||||
|
||||
private static boolean containsViewport(SharedPreferences sharedPreferences) {
|
||||
return sharedPreferences.contains(KEY_LATITUDE)
|
||||
&& sharedPreferences.contains(KEY_LONGITUDE)
|
||||
&& sharedPreferences.contains(KEY_MAP_SCALE);
|
||||
}
|
||||
|
||||
protected Map mMap;
|
||||
protected MapView mMapView;
|
||||
|
||||
public Map map() {
|
||||
return mMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mMap.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit();
|
||||
editor.clear();
|
||||
|
||||
// save the map position
|
||||
MapPosition mapPosition = new MapPosition();
|
||||
|
||||
mMap.viewport().getMapPosition(mapPosition);
|
||||
|
||||
GeoPoint geoPoint = mapPosition.getGeoPoint();
|
||||
|
||||
editor.putInt(KEY_LATITUDE, geoPoint.latitudeE6);
|
||||
editor.putInt(KEY_LONGITUDE, geoPoint.longitudeE6);
|
||||
editor.putFloat(KEY_MAP_SCALE, (float) mapPosition.scale);
|
||||
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mMapView.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mMapView.onPause();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called once by each MapView during its setup process.
|
||||
*
|
||||
* @param mapView the calling MapView.
|
||||
*/
|
||||
public final void registerMapView(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mMap = mapView.map();
|
||||
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES_FILE,
|
||||
MODE_PRIVATE);
|
||||
|
||||
if (containsViewport(sharedPreferences)) {
|
||||
// get and set the map position and zoom level
|
||||
int latitudeE6 = sharedPreferences.getInt(KEY_LATITUDE, 0);
|
||||
int longitudeE6 = sharedPreferences.getInt(KEY_LONGITUDE, 0);
|
||||
float scale = sharedPreferences.getFloat(KEY_MAP_SCALE, 1);
|
||||
|
||||
MapPosition mapPosition = new MapPosition();
|
||||
mapPosition.setPosition(latitudeE6 / 1E6, longitudeE6 / 1E6);
|
||||
mapPosition.setScale(scale);
|
||||
|
||||
mMap.setMapPosition(mapPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
189
vtm-app/src/org/oscim/app/MapLayers.java
Normal file
189
vtm-app/src/org/oscim/app/MapLayers.java
Normal file
@@ -0,0 +1,189 @@
|
||||
package org.oscim.app;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.oscim.android.cache.TileCache;
|
||||
import org.oscim.layers.GenericLayer;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.layers.TileGridLayer;
|
||||
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
|
||||
import org.oscim.layers.tile.buildings.BuildingLayer;
|
||||
import org.oscim.layers.tile.vector.VectorTileLayer;
|
||||
import org.oscim.layers.tile.vector.labeling.LabelLayer;
|
||||
import org.oscim.theme.ThemeFile;
|
||||
import org.oscim.theme.VtmThemes;
|
||||
import org.oscim.tiling.ITileCache;
|
||||
import org.oscim.tiling.TileSource;
|
||||
import org.oscim.tiling.source.UrlTileSource;
|
||||
import org.oscim.tiling.source.bitmap.DefaultSources;
|
||||
import org.oscim.tiling.source.mapfile.MapFileTileSource;
|
||||
import org.oscim.tiling.source.mapnik.MapnikVectorTileSource;
|
||||
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MapLayers {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(MapLayers.class);
|
||||
|
||||
private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/";
|
||||
|
||||
abstract static class Config {
|
||||
final String name;
|
||||
|
||||
public Config(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
abstract TileSource init();
|
||||
}
|
||||
|
||||
static Config[] configs = new Config[]{new Config("OPENSCIENCEMAP4") {
|
||||
TileSource init() {
|
||||
return new OSciMap4TileSource();
|
||||
}
|
||||
}, new Config("MAPSFORGE") {
|
||||
TileSource init() {
|
||||
return new MapFileTileSource().setOption("file",
|
||||
"/storage/sdcard0/germany.map");
|
||||
}
|
||||
}, new Config("MAPNIK_VECTOR") {
|
||||
TileSource init() {
|
||||
return new MapnikVectorTileSource();
|
||||
}
|
||||
}};
|
||||
|
||||
private VectorTileLayer mBaseLayer;
|
||||
private String mMapDatabase;
|
||||
private ITileCache mCache;
|
||||
|
||||
private GenericLayer mGridOverlay;
|
||||
private boolean mGridEnabled;
|
||||
|
||||
// FIXME -> implement LayerGroup
|
||||
private int mBackgroundId = -2;
|
||||
private Layer mBackroundPlaceholder;
|
||||
private Layer mBackgroundLayer;
|
||||
|
||||
public MapLayers() {
|
||||
mBackroundPlaceholder = new Layer(null) {
|
||||
};
|
||||
setBackgroundMap(-1);
|
||||
}
|
||||
|
||||
void setBaseMap(SharedPreferences preferences) {
|
||||
String dbname = preferences.getString("mapDatabase", "OPENSCIENCEMAP4");
|
||||
|
||||
if (dbname.equals(mMapDatabase) && mBaseLayer != null)
|
||||
return;
|
||||
|
||||
TileSource tileSource = null;
|
||||
for (Config c : configs)
|
||||
if (c.name.equals(dbname))
|
||||
tileSource = c.init();
|
||||
|
||||
if (tileSource == null) {
|
||||
tileSource = configs[0].init();
|
||||
dbname = configs[0].name;
|
||||
preferences.edit().putString("mapDatabase", dbname).commit();
|
||||
}
|
||||
|
||||
if (tileSource instanceof UrlTileSource) {
|
||||
mCache = new TileCache(App.activity, CACHE_DIRECTORY, dbname);
|
||||
mCache.setCacheSize(512 * (1 << 10));
|
||||
tileSource.setCache(mCache);
|
||||
} else {
|
||||
mCache = null;
|
||||
}
|
||||
|
||||
if (mBaseLayer == null) {
|
||||
mBaseLayer = App.map.setBaseMap(tileSource);
|
||||
App.map.layers().add(2, new BuildingLayer(App.map, mBaseLayer));
|
||||
App.map.layers().add(3, new LabelLayer(App.map, mBaseLayer));
|
||||
} else
|
||||
mBaseLayer.setTileSource(tileSource);
|
||||
|
||||
mMapDatabase = dbname;
|
||||
}
|
||||
|
||||
void setPreferences(SharedPreferences preferences) {
|
||||
setBaseMap(preferences);
|
||||
|
||||
ThemeFile theme = VtmThemes.DEFAULT;
|
||||
if (preferences.contains("theme")) {
|
||||
String name = preferences.getString("theme", "DEFAULT");
|
||||
try {
|
||||
theme = VtmThemes.valueOf(name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
theme = VtmThemes.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
App.map.setTheme(theme);
|
||||
|
||||
// default cache size 20MB
|
||||
int cacheSize = preferences.getInt("cacheSize", 20);
|
||||
|
||||
if (mCache != null)
|
||||
mCache.setCacheSize(cacheSize * (1 << 20));
|
||||
|
||||
}
|
||||
|
||||
void enableGridOverlay(boolean enable) {
|
||||
if (mGridEnabled == enable)
|
||||
return;
|
||||
|
||||
if (enable) {
|
||||
if (mGridOverlay == null)
|
||||
mGridOverlay = new TileGridLayer(App.map);
|
||||
|
||||
App.map.layers().add(mGridOverlay);
|
||||
} else {
|
||||
App.map.layers().remove(mGridOverlay);
|
||||
}
|
||||
|
||||
mGridEnabled = enable;
|
||||
App.map.updateMap(true);
|
||||
}
|
||||
|
||||
boolean isGridEnabled() {
|
||||
return mGridEnabled;
|
||||
}
|
||||
|
||||
void setBackgroundMap(int id) {
|
||||
if (id == mBackgroundId)
|
||||
return;
|
||||
|
||||
App.map.layers().remove(mBackgroundLayer);
|
||||
mBackgroundLayer = null;
|
||||
|
||||
switch (id) {
|
||||
case R.id.menu_layer_openstreetmap:
|
||||
mBackgroundLayer = new BitmapTileLayer(App.map, DefaultSources.OPENSTREETMAP.build());
|
||||
break;
|
||||
|
||||
case R.id.menu_layer_naturalearth:
|
||||
mBackgroundLayer = new BitmapTileLayer(App.map, DefaultSources.NE_LANDCOVER.build());
|
||||
break;
|
||||
default:
|
||||
mBackgroundLayer = mBackroundPlaceholder;
|
||||
id = -1;
|
||||
}
|
||||
|
||||
if (mBackgroundLayer instanceof BitmapTileLayer)
|
||||
App.map.setBaseMap((BitmapTileLayer) mBackgroundLayer);
|
||||
else
|
||||
App.map.layers().add(1, mBackroundPlaceholder);
|
||||
|
||||
mBackgroundId = id;
|
||||
}
|
||||
|
||||
int getBackgroundId() {
|
||||
return mBackgroundId;
|
||||
}
|
||||
|
||||
public void deleteCache() {
|
||||
if (mCache != null)
|
||||
mCache.setCacheSize(0);
|
||||
}
|
||||
}
|
||||
309
vtm-app/src/org/oscim/app/POIActivity.java
Normal file
309
vtm-app/src/org/oscim/app/POIActivity.java
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright 2012 osmdroidbonuspack: M.Kergall
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.DataSetObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.osmdroid.location.FourSquareProvider;
|
||||
import org.osmdroid.location.POI;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Activity showing POIs as a list.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
|
||||
// TODO implement:
|
||||
// http://codehenge.net/blog/2011/06/android-development-tutorial-
|
||||
// asynchronous-lazy-loading-and-caching-of-listview-images/
|
||||
|
||||
public class POIActivity extends Activity {
|
||||
|
||||
AutoCompleteTextView poiTagText;
|
||||
POIAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.items_list);
|
||||
|
||||
ListView list = (ListView) findViewById(R.id.items);
|
||||
|
||||
Intent myIntent = getIntent();
|
||||
|
||||
final List<POI> pois = App.poiSearch.getPOIs();
|
||||
final int currentNodeId = myIntent.getIntExtra("ID", -1);
|
||||
POIAdapter adapter = new POIAdapter(this, pois);
|
||||
mAdapter = adapter;
|
||||
|
||||
list.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> arg0, View view, int position, long index) {
|
||||
//log.debug("poi on click: " + position);
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("ID", position);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
list.setAdapter(adapter);
|
||||
list.setSelection(currentNodeId);
|
||||
|
||||
// POI search interface:
|
||||
String[] poiTags = getResources().getStringArray(R.array.poi_tags);
|
||||
poiTagText = (AutoCompleteTextView) findViewById(R.id.poiTag);
|
||||
ArrayAdapter<String> textadapter = new ArrayAdapter<String>(this,
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
poiTags);
|
||||
poiTagText.setAdapter(textadapter);
|
||||
|
||||
// Button setPOITagButton = (Button) findViewById(R.id.buttonSetPOITag);
|
||||
// setPOITagButton.setOnClickListener(new View.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
// hideKeyboard();
|
||||
// //Start search:
|
||||
// App.poiSearch.getPOIAsync(poiTagText.getText().toString());
|
||||
// }
|
||||
// });
|
||||
|
||||
// FIXME!
|
||||
Button btn = (Button) findViewById(R.id.pois_btn_flickr);
|
||||
btn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hideKeyboard();
|
||||
App.poiSearch.getPOIAsync(POISearch.TAG_FLICKR);
|
||||
}
|
||||
});
|
||||
|
||||
btn = (Button) findViewById(R.id.pois_btn_nominatim);
|
||||
btn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hideKeyboard();
|
||||
String text = poiTagText.getText().toString();
|
||||
if (text == null || text.length() == 0)
|
||||
App.poiSearch.getPOIAsync("bremen");
|
||||
else
|
||||
App.poiSearch.getPOIAsync(text);
|
||||
}
|
||||
});
|
||||
|
||||
btn = (Button) findViewById(R.id.pois_btn_wikipedia);
|
||||
btn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hideKeyboard();
|
||||
App.poiSearch.getPOIAsync(POISearch.TAG_WIKIPEDIA);
|
||||
}
|
||||
});
|
||||
|
||||
btn = (Button) findViewById(R.id.pois_btn_foursquare);
|
||||
btn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hideKeyboard();
|
||||
App.poiSearch.getPOIAsync(POISearch.TAG_FOURSQUARE
|
||||
+ poiTagText.getText().toString());
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(list);
|
||||
|
||||
// only show keyboard when nothing in the list yet
|
||||
if (pois == null || pois.size() == 0) {
|
||||
poiTagText.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputMethodManager keyboard = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
keyboard.showSoftInput(poiTagText, 0);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
private void hideKeyboard() {
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(poiTagText.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
// from SearchableDictionary Example:
|
||||
// Because this activity has set launchMode="singleTop", the system calls this method
|
||||
// to deliver the intent if this activity is currently the foreground activity when
|
||||
// invoked again (when the user executes a search from this activity, we don't create
|
||||
// a new instance of this activity, so the system delivers the search intent here)
|
||||
// handleIntent(intent);
|
||||
|
||||
// final ArrayList<POI> pois = intent.getParcelableArrayListExtra("POI");
|
||||
// final int currentNodeId = intent.getIntExtra("ID", -1);
|
||||
// POIAdapter adapter = new POIAdapter(this, pois);
|
||||
// mAdapter.setPOI(pois);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
if (v.getId() == R.id.items) {
|
||||
//AdapterView.AdapterContextMenuInfo info =
|
||||
// (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
//log.debug("list context menu created " + info.position);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.poi_menu, menu);
|
||||
|
||||
}
|
||||
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
//log.debug("context menu item selected " + item.getItemId());
|
||||
|
||||
if (item.getItemId() == R.id.menu_link) {
|
||||
|
||||
AdapterView.AdapterContextMenuInfo info =
|
||||
(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
|
||||
|
||||
POI poi = (POI) mAdapter.getItem(info.position);
|
||||
if (poi == null || poi.url == null)
|
||||
return false;
|
||||
|
||||
if (poi.serviceId == POI.POI_SERVICE_4SQUARE) {
|
||||
FourSquareProvider.browse(this, poi);
|
||||
return true;
|
||||
} else {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
i.setData(Uri.parse(poi.url));
|
||||
startActivity(i);
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
class POIObserver extends DataSetObserver {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class POIAdapter extends BaseAdapter implements OnClickListener {
|
||||
private Context mContext;
|
||||
private final List<POI> mPois;
|
||||
|
||||
public POIAdapter(Context context, List<POI> pois) {
|
||||
mContext = context;
|
||||
mPois = pois;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mPois == null)
|
||||
return 0;
|
||||
|
||||
return mPois.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (mPois == null)
|
||||
return null;
|
||||
|
||||
return mPois.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup viewGroup) {
|
||||
POI entry = (POI) getItem(position);
|
||||
if (view == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) mContext
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
view = inflater.inflate(R.layout.item_layout, null);
|
||||
|
||||
ViewHolder holder = new ViewHolder();
|
||||
holder.title = (TextView) view.findViewById(R.id.title);
|
||||
holder.details = (TextView) view.findViewById(R.id.details);
|
||||
holder.thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
|
||||
view.setTag(holder);
|
||||
}
|
||||
|
||||
ViewHolder holder = (ViewHolder) view.getTag();
|
||||
|
||||
holder.title.setText((entry.url == null ? "" : "[link] ") + entry.type);
|
||||
holder.details.setText(entry.description);
|
||||
|
||||
entry.fetchThumbnail(holder.thumbnail);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
//nothing to do.
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
public TextView title;
|
||||
public TextView details;
|
||||
public ImageView thumbnail;
|
||||
}
|
||||
}
|
||||
320
vtm-app/src/org/oscim/app/POISearch.java
Normal file
320
vtm-app/src/org/oscim/app/POISearch.java
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright 2012 osmdroid: M.Kergall
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.oscim.android.canvas.AndroidGraphics;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.layers.marker.MarkerItem.HotspotPlace;
|
||||
import org.oscim.layers.marker.MarkerSymbol;
|
||||
import org.oscim.map.Map;
|
||||
import org.osmdroid.location.FlickrPOIProvider;
|
||||
import org.osmdroid.location.FourSquareProvider;
|
||||
import org.osmdroid.location.GeoNamesPOIProvider;
|
||||
import org.osmdroid.location.NominatimPOIProvider;
|
||||
import org.osmdroid.location.POI;
|
||||
import org.osmdroid.location.PicasaPOIProvider;
|
||||
import org.osmdroid.overlays.DefaultInfoWindow;
|
||||
import org.osmdroid.overlays.ExtendedMarkerItem;
|
||||
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class POISearch {
|
||||
private final ArrayList<POI> mPOIs;
|
||||
ItemizedOverlayWithBubble<ExtendedMarkerItem> poiMarkers;
|
||||
MarkerSymbol[] mMarkers;
|
||||
|
||||
private final static int MDEFAULT = 0;
|
||||
private final static int MFLICKR = 1;
|
||||
private final static int MPICASA = 2;
|
||||
private final static int MWIKI16 = 3;
|
||||
private final static int MWIKI32 = 4;
|
||||
|
||||
POISearch() {
|
||||
mPOIs = new ArrayList<POI>();
|
||||
//POI markers:
|
||||
final ArrayList<ExtendedMarkerItem> poiItems = new ArrayList<ExtendedMarkerItem>();
|
||||
|
||||
poiMarkers = new ItemizedOverlayWithBubble<ExtendedMarkerItem>(App.map,
|
||||
App.activity,
|
||||
null,
|
||||
poiItems,
|
||||
new POIInfoWindow(App.map));
|
||||
|
||||
App.map.layers().add(poiMarkers);
|
||||
|
||||
mMarkers = new MarkerSymbol[5];
|
||||
|
||||
mMarkers[MDEFAULT] = AndroidGraphics
|
||||
.makeMarker(App.res.getDrawable(R.drawable.pin), HotspotPlace.BOTTOM_CENTER);
|
||||
|
||||
mMarkers[MFLICKR] = AndroidGraphics
|
||||
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_flickr), null);
|
||||
|
||||
mMarkers[MPICASA] = AndroidGraphics
|
||||
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_picasa_24), null);
|
||||
|
||||
mMarkers[MWIKI16] = AndroidGraphics
|
||||
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_wikipedia_16), null);
|
||||
|
||||
mMarkers[MWIKI32] = AndroidGraphics
|
||||
.makeMarker(App.res.getDrawable(R.drawable.marker_poi_wikipedia_32), null);
|
||||
}
|
||||
|
||||
public List<POI> getPOIs() {
|
||||
return mPOIs;
|
||||
}
|
||||
|
||||
final static String TAG_WIKIPEDIA = "wikipedia";
|
||||
final static String TAG_FLICKR = "flickr";
|
||||
final static String TAG_PICASA = "picasa";
|
||||
final static String TAG_FOURSQUARE = "foursquare";
|
||||
|
||||
//private final static String TAG_NOMINATIM = "nominatim";
|
||||
|
||||
class POITask extends AsyncTask<Object, Void, List<POI>> {
|
||||
String mTag;
|
||||
|
||||
@Override
|
||||
protected List<POI> doInBackground(Object... params) {
|
||||
mTag = (String) params[0];
|
||||
|
||||
if (mTag == null || mTag.equals("")) {
|
||||
return null;
|
||||
}
|
||||
BoundingBox bb = App.map.getBoundingBox(0);
|
||||
|
||||
if (mTag.equals(TAG_WIKIPEDIA)) {
|
||||
GeoNamesPOIProvider poiProvider = new GeoNamesPOIProvider("mkergall");
|
||||
//ArrayList<POI> pois = poiProvider.getPOICloseTo(point, 30, 20.0);
|
||||
//Get POI inside the bounding box of the current map view:
|
||||
|
||||
return poiProvider.getPOIInside(bb, 30);
|
||||
|
||||
//OverpassPOIProvider poiProvider = new OverpassPOIProvider();
|
||||
//return poiProvider.getPOIInside(bb, "", 0);
|
||||
} else if (mTag.equals(TAG_FLICKR)) {
|
||||
FlickrPOIProvider poiProvider = new FlickrPOIProvider("c39be46304a6c6efda8bc066c185cd7e");
|
||||
return poiProvider.getPOIInside(bb, null, 20);
|
||||
} else if (mTag.startsWith(TAG_PICASA)) {
|
||||
PicasaPOIProvider poiProvider = new PicasaPOIProvider(null);
|
||||
String q = mTag.substring(7);
|
||||
return poiProvider.getPOIInside(bb, q, 20);
|
||||
} else if (mTag.startsWith(TAG_FOURSQUARE)) {
|
||||
FourSquareProvider poiProvider = new FourSquareProvider(null, null);
|
||||
String q = mTag.substring(10);
|
||||
// String q = mTag.substring("picasa".length());
|
||||
return poiProvider.getPOIInside(bb, q, 40);
|
||||
} else {
|
||||
NominatimPOIProvider poiProvider = new NominatimPOIProvider();
|
||||
//poiProvider.setService(NominatimPOIProvider.NOMINATIM_POI_SERVICE);
|
||||
|
||||
poiProvider.setService(NominatimPOIProvider.MAPQUEST_POI_SERVICE);
|
||||
|
||||
//pois = poiProvider.getPOIAlong(mRoute.getRouteLow(), mTag, 100, 2.0);
|
||||
return poiProvider.getPOIInside(bb, mTag, 10);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<POI> pois) {
|
||||
if (mTag.equals("")) {
|
||||
//no search, no message
|
||||
} else if (pois == null) {
|
||||
Toast.makeText(App.activity,
|
||||
"Technical issue when getting " + mTag + " POI.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(App.activity,
|
||||
pois.size() + " " + mTag + " entries found",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
// if (mTag.equals("flickr") || mTag.startsWith("picasa") || mTag.equals("wikipedia"))
|
||||
// startAsyncThumbnailsLoading(mPOIs);
|
||||
}
|
||||
|
||||
updateUIWithPOI(pois);
|
||||
}
|
||||
}
|
||||
|
||||
void updateUIWithPOI(List<POI> pois) {
|
||||
mPOIs.clear();
|
||||
if (pois == null) {
|
||||
showPOIActivity(true);
|
||||
App.map.updateMap(true);
|
||||
return;
|
||||
}
|
||||
|
||||
mPOIs.addAll(pois);
|
||||
|
||||
for (POI poi : pois) {
|
||||
String desc = null;
|
||||
String name = null;
|
||||
|
||||
if (poi.serviceId == POI.POI_SERVICE_NOMINATIM) {
|
||||
name = poi.description;
|
||||
String[] split = name.split(", ");
|
||||
if (split != null && split.length > 1) {
|
||||
name = split[0];
|
||||
desc = split[1];
|
||||
|
||||
for (int i = 2; i < 3 && i < split.length; i++)
|
||||
desc += "," + split[i];
|
||||
}
|
||||
|
||||
} else {
|
||||
desc = poi.description;
|
||||
}
|
||||
|
||||
ExtendedMarkerItem poiMarker =
|
||||
new ExtendedMarkerItem(poi.type + (name == null ? "" : ": " + name), desc,
|
||||
poi.location);
|
||||
MarkerSymbol marker = null;
|
||||
|
||||
if (poi.serviceId == POI.POI_SERVICE_NOMINATIM) {
|
||||
|
||||
marker = mMarkers[MDEFAULT];
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_GEONAMES_WIKIPEDIA) {
|
||||
if (poi.rank < 90)
|
||||
marker = mMarkers[MWIKI16];
|
||||
else
|
||||
marker = mMarkers[MWIKI32];
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_FLICKR) {
|
||||
marker = mMarkers[MFLICKR];
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_PICASA) {
|
||||
marker = mMarkers[MPICASA];
|
||||
poiMarker.setSubDescription(poi.category);
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_4SQUARE) {
|
||||
marker = mMarkers[MDEFAULT];
|
||||
poiMarker.setSubDescription(poi.category);
|
||||
}
|
||||
|
||||
poiMarker.setMarker(marker);
|
||||
//thumbnail loading moved in POIInfoWindow.onOpen for better performances.
|
||||
poiMarker.setRelatedObject(poi);
|
||||
poiMarkers.addItem(poiMarker);
|
||||
}
|
||||
|
||||
showPOIActivity(true);
|
||||
App.map.updateMap(true);
|
||||
}
|
||||
|
||||
private void showPOIActivity(boolean setNew) {
|
||||
// show or update
|
||||
Intent intent = new Intent(App.activity, POIActivity.class);
|
||||
intent.putExtra("ID", poiMarkers.getBubbledItemId());
|
||||
if (setNew)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
App.activity.startActivityForResult(intent, TileMap.POIS_REQUEST);
|
||||
}
|
||||
|
||||
void getPOIAsync(String tag) {
|
||||
poiMarkers.removeAllItems();
|
||||
new POITask().execute(tag);
|
||||
}
|
||||
|
||||
class POIInfoWindow extends DefaultInfoWindow {
|
||||
|
||||
private Button mButton;
|
||||
private ImageView mImage;
|
||||
|
||||
public POIInfoWindow(Map map) {
|
||||
super(R.layout.bonuspack_bubble, App.view);
|
||||
|
||||
mButton = (Button) mView.findViewById(R.id.bubble_moreinfo);
|
||||
mImage = (ImageView) mView.findViewById(R.id.bubble_image);
|
||||
|
||||
//bonuspack_bubble layouts already contain a "more info" button.
|
||||
mButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
POI poi = (POI) view.getTag();
|
||||
|
||||
if (poi == null)
|
||||
return;
|
||||
|
||||
if (poi.serviceId == POI.POI_SERVICE_4SQUARE) {
|
||||
FourSquareProvider.browse(view.getContext(), poi);
|
||||
} else if (poi.url != null) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(poi.url));
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
view.getContext().startActivity(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getView().setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
POI poi = (POI) view.getTag();
|
||||
|
||||
if (poi != null)
|
||||
showPOIActivity(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ExtendedMarkerItem item) {
|
||||
POI poi = (POI) item.getRelatedObject();
|
||||
|
||||
super.onOpen(item);
|
||||
|
||||
poi.fetchThumbnail(mImage);
|
||||
|
||||
//Show or hide "more info" button:
|
||||
if (poi.url != null)
|
||||
mButton.setVisibility(View.VISIBLE);
|
||||
else
|
||||
mButton.setVisibility(View.GONE);
|
||||
|
||||
mButton.setTag(poi);
|
||||
getView().setTag(poi);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onContextItemSelected(MenuItem item, GeoPoint geoPoint) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_poi_nearby:
|
||||
Intent intent = new Intent(App.activity, POIActivity.class);
|
||||
intent.putExtra("ID", poiMarkers.getBubbledItemId());
|
||||
App.activity.startActivityForResult(intent, TileMap.POIS_REQUEST);
|
||||
return true;
|
||||
|
||||
case R.id.menu_poi_clear:
|
||||
poiMarkers.removeAllItems();
|
||||
mPOIs.clear();
|
||||
App.map.updateMap(true);
|
||||
|
||||
return true;
|
||||
default:
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
452
vtm-app/src/org/oscim/app/RouteSearch.java
Normal file
452
vtm-app/src/org/oscim/app/RouteSearch.java
Normal file
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright 2012 osmdroid: M.Kergall
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.location.Address;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.oscim.android.canvas.AndroidGraphics;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.layers.PathLayer;
|
||||
import org.oscim.layers.marker.MarkerItem.HotspotPlace;
|
||||
import org.oscim.layers.marker.MarkerSymbol;
|
||||
import org.osmdroid.location.GeocoderNominatim;
|
||||
import org.osmdroid.overlays.DefaultInfoWindow;
|
||||
import org.osmdroid.overlays.ExtendedMarkerItem;
|
||||
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
|
||||
import org.osmdroid.routing.Route;
|
||||
import org.osmdroid.routing.RouteProvider;
|
||||
import org.osmdroid.routing.provider.OSRMRouteProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RouteSearch {
|
||||
private static int START_INDEX = -2, DEST_INDEX = -1;
|
||||
|
||||
private final PathLayer mRouteOverlay;
|
||||
//private final ItemizedOverlayWithBubble<ExtendedOverlayItem> mRouteMarkers;
|
||||
private final ItemizedOverlayWithBubble<ExtendedMarkerItem> mItineraryMarkers;
|
||||
|
||||
private final RouteBar mRouteBar;
|
||||
|
||||
private GeoPoint mStartPoint, mDestinationPoint;
|
||||
private final ArrayList<GeoPoint> mViaPoints;
|
||||
|
||||
private ExtendedMarkerItem markerStart, markerDestination;
|
||||
|
||||
private UpdateRouteTask mRouteTask;
|
||||
|
||||
RouteSearch() {
|
||||
mViaPoints = new ArrayList<GeoPoint>();
|
||||
|
||||
// Itinerary markers:
|
||||
ArrayList<ExtendedMarkerItem> waypointsItems = new ArrayList<ExtendedMarkerItem>();
|
||||
|
||||
mItineraryMarkers = new ItemizedOverlayWithBubble<ExtendedMarkerItem>(App.map,
|
||||
App.activity,
|
||||
null,
|
||||
waypointsItems,
|
||||
new ViaPointInfoWindow(R.layout.itinerary_bubble));
|
||||
|
||||
//updateIternaryMarkers();
|
||||
|
||||
//Route and Directions
|
||||
//ArrayList<ExtendedOverlayItem> routeItems = new ArrayList<ExtendedOverlayItem>();
|
||||
//mRouteMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(App.map, App.activity,
|
||||
// null, routeItems);
|
||||
|
||||
mRouteOverlay = new PathLayer(App.map, 0xAA0000FF, 3);
|
||||
|
||||
// TODO use LayerGroup
|
||||
App.map.layers().add(mRouteOverlay);
|
||||
//App.map.getOverlays().add(mRouteMarkers);
|
||||
App.map.layers().add(mItineraryMarkers);
|
||||
|
||||
mRouteBar = new RouteBar(App.activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve route between p1 and p2 and update overlays.
|
||||
*/
|
||||
public void showRoute(GeoPoint p1, GeoPoint p2) {
|
||||
clearOverlays();
|
||||
|
||||
mStartPoint = p1;
|
||||
markerStart = putMarkerItem(markerStart, mStartPoint, START_INDEX,
|
||||
R.string.departure, R.drawable.marker_departure, -1);
|
||||
|
||||
mDestinationPoint = p2;
|
||||
markerDestination = putMarkerItem(markerDestination, mDestinationPoint, DEST_INDEX,
|
||||
R.string.destination,
|
||||
R.drawable.marker_destination, -1);
|
||||
|
||||
getRouteAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse Geocoding
|
||||
*/
|
||||
public String getAddress(GeoPoint p) {
|
||||
GeocoderNominatim geocoder = new GeocoderNominatim(App.activity);
|
||||
String theAddress;
|
||||
try {
|
||||
double dLatitude = p.getLatitude();
|
||||
double dLongitude = p.getLongitude();
|
||||
List<Address> addresses = geocoder.getFromLocation(dLatitude, dLongitude, 1);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (addresses.size() > 0) {
|
||||
Address address = addresses.get(0);
|
||||
int n = address.getMaxAddressLineIndex();
|
||||
for (int i = 0; i <= n; i++) {
|
||||
if (i != 0)
|
||||
sb.append(", ");
|
||||
sb.append(address.getAddressLine(i));
|
||||
}
|
||||
theAddress = new String(sb.toString());
|
||||
} else {
|
||||
theAddress = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
theAddress = null;
|
||||
}
|
||||
if (theAddress != null) {
|
||||
return theAddress;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Async task to reverse-geocode the marker position in a separate thread:
|
||||
class GeocodingTask extends AsyncTask<Object, Void, String> {
|
||||
ExtendedMarkerItem marker;
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Object... params) {
|
||||
marker = (ExtendedMarkerItem) params[0];
|
||||
return getAddress(marker.getPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
marker.setDescription(result);
|
||||
}
|
||||
}
|
||||
|
||||
/* add (or replace) an item in markerOverlays. p position. */
|
||||
public ExtendedMarkerItem putMarkerItem(ExtendedMarkerItem item, GeoPoint p, int index,
|
||||
int titleResId, int markerResId, int iconResId) {
|
||||
|
||||
if (item != null)
|
||||
mItineraryMarkers.removeItem(item);
|
||||
|
||||
MarkerSymbol marker = AndroidGraphics.makeMarker(App.res.getDrawable(markerResId),
|
||||
HotspotPlace.BOTTOM_CENTER);
|
||||
|
||||
ExtendedMarkerItem overlayItem =
|
||||
new ExtendedMarkerItem(App.res.getString(titleResId), "", p);
|
||||
|
||||
overlayItem.setMarker(marker);
|
||||
|
||||
if (iconResId != -1)
|
||||
overlayItem.setImage(App.res.getDrawable(iconResId));
|
||||
|
||||
overlayItem.setRelatedObject(Integer.valueOf(index));
|
||||
|
||||
mItineraryMarkers.addItem(overlayItem);
|
||||
|
||||
App.map.updateMap(true);
|
||||
|
||||
//Start geocoding task to update the description of the marker with its address:
|
||||
new GeocodingTask().execute(overlayItem);
|
||||
|
||||
return overlayItem;
|
||||
}
|
||||
|
||||
public void addViaPoint(GeoPoint p) {
|
||||
mViaPoints.add(p);
|
||||
putMarkerItem(null, p, mViaPoints.size() - 1,
|
||||
R.string.viapoint, R.drawable.marker_via, -1);
|
||||
}
|
||||
|
||||
public void removePoint(int index) {
|
||||
if (index == START_INDEX) {
|
||||
mStartPoint = null;
|
||||
} else if (index == DEST_INDEX) {
|
||||
mDestinationPoint = null;
|
||||
} else
|
||||
mViaPoints.remove(index);
|
||||
|
||||
getRouteAsync();
|
||||
updateIternaryMarkers();
|
||||
}
|
||||
|
||||
public void updateIternaryMarkers() {
|
||||
mItineraryMarkers.removeAllItems();
|
||||
|
||||
//Start marker:
|
||||
if (mStartPoint != null) {
|
||||
markerStart = putMarkerItem(null, mStartPoint, START_INDEX,
|
||||
R.string.departure, R.drawable.marker_departure, -1);
|
||||
}
|
||||
//Via-points markers if any:
|
||||
for (int index = 0; index < mViaPoints.size(); index++) {
|
||||
putMarkerItem(null, mViaPoints.get(index), index,
|
||||
R.string.viapoint, R.drawable.marker_via, -1);
|
||||
}
|
||||
//Destination marker if any:
|
||||
if (mDestinationPoint != null) {
|
||||
markerDestination = putMarkerItem(null, mDestinationPoint, DEST_INDEX,
|
||||
R.string.destination,
|
||||
R.drawable.marker_destination, -1);
|
||||
}
|
||||
}
|
||||
|
||||
//------------ Route and Directions
|
||||
private void updateOverlays(Route route) {
|
||||
//mRouteMarkers.removeAllItems();
|
||||
|
||||
mRouteOverlay.clearPath();
|
||||
|
||||
if (route == null || route.status == Route.STATUS_DEFAULT) {
|
||||
App.activity.showToastOnUiThread(App.res.getString(R.string.route_lookup_error));
|
||||
return;
|
||||
}
|
||||
|
||||
mRouteOverlay.setPoints(route.routeHigh);
|
||||
|
||||
//OverlayMarker marker = AndroidGraphics.makeMarker(App.res, R.drawable.marker_node, null);
|
||||
|
||||
//int n = route.nodes.size();
|
||||
//for (int i = 0; i < n; i++) {
|
||||
// RouteNode node = route.nodes.get(i);
|
||||
// String instructions = (node.instructions == null ? "" : node.instructions);
|
||||
// ExtendedOverlayItem nodeMarker = new ExtendedOverlayItem(
|
||||
// "Step " + (i + 1), instructions, node.location);
|
||||
//
|
||||
// nodeMarker.setSubDescription(route.getLengthDurationText(node.length, node.duration));
|
||||
// nodeMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
|
||||
// nodeMarker.setMarker(marker);
|
||||
//
|
||||
// mRouteMarkers.addItem(nodeMarker);
|
||||
//}
|
||||
|
||||
App.map.updateMap(true);
|
||||
}
|
||||
|
||||
void clearOverlays() {
|
||||
//mRouteMarkers.removeAllItems(true);
|
||||
mItineraryMarkers.removeAllItems(true);
|
||||
|
||||
mRouteOverlay.clearPath();
|
||||
mStartPoint = null;
|
||||
mDestinationPoint = null;
|
||||
mViaPoints.clear();
|
||||
|
||||
App.map.updateMap(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async task to get the route in a separate thread.
|
||||
*/
|
||||
class UpdateRouteTask extends AsyncTask<List<GeoPoint>, Void, Route> {
|
||||
@Override
|
||||
protected Route doInBackground(List<GeoPoint>... wp) {
|
||||
List<GeoPoint> waypoints = wp[0];
|
||||
|
||||
//RouteProvider routeProvider = new MapQuestRouteProvider();
|
||||
//Locale locale = Locale.getDefault();
|
||||
//routeProvider.addRequestOption("locale=" + locale.getLanguage() + "_"
|
||||
// + locale.getCountry());
|
||||
//routeProvider.addRequestOption("routeType=pedestrian");
|
||||
|
||||
//RouteProvider routeProvider = new GoogleRouteProvider();
|
||||
RouteProvider routeProvider = new OSRMRouteProvider();
|
||||
return routeProvider.getRoute(waypoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Route result) {
|
||||
|
||||
updateOverlays(result);
|
||||
mRouteBar.set(result);
|
||||
|
||||
mRouteTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getRouteAsync() {
|
||||
if (mRouteTask != null) {
|
||||
mRouteTask.cancel(true);
|
||||
mRouteTask = null;
|
||||
}
|
||||
|
||||
if (mStartPoint == null || mDestinationPoint == null) {
|
||||
mRouteOverlay.clearPath();
|
||||
return;
|
||||
}
|
||||
|
||||
List<GeoPoint> waypoints = new ArrayList<GeoPoint>();
|
||||
waypoints.add(mStartPoint);
|
||||
//add intermediate via points:
|
||||
for (GeoPoint p : mViaPoints) {
|
||||
waypoints.add(p);
|
||||
}
|
||||
waypoints.add(mDestinationPoint);
|
||||
|
||||
mRouteTask = new UpdateRouteTask();
|
||||
mRouteTask.execute(waypoints);
|
||||
}
|
||||
|
||||
boolean onContextItemSelected(MenuItem item, GeoPoint geoPoint) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_route_departure:
|
||||
mStartPoint = geoPoint;
|
||||
|
||||
markerStart = putMarkerItem(markerStart, mStartPoint, START_INDEX,
|
||||
R.string.departure, R.drawable.marker_departure, -1);
|
||||
|
||||
getRouteAsync();
|
||||
return true;
|
||||
|
||||
case R.id.menu_route_destination:
|
||||
mDestinationPoint = geoPoint;
|
||||
|
||||
markerDestination = putMarkerItem(markerDestination, mDestinationPoint, DEST_INDEX,
|
||||
R.string.destination,
|
||||
R.drawable.marker_destination, -1);
|
||||
|
||||
getRouteAsync();
|
||||
return true;
|
||||
|
||||
case R.id.menu_route_viapoint:
|
||||
GeoPoint viaPoint = geoPoint;
|
||||
addViaPoint(viaPoint);
|
||||
|
||||
getRouteAsync();
|
||||
return true;
|
||||
|
||||
case R.id.menu_route_clear:
|
||||
clearOverlays();
|
||||
return true;
|
||||
|
||||
default:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (mItineraryMarkers.size() == 0);
|
||||
}
|
||||
|
||||
class ViaPointInfoWindow extends DefaultInfoWindow {
|
||||
|
||||
int mSelectedPoint;
|
||||
|
||||
public ViaPointInfoWindow(int layoutResId) {
|
||||
super(layoutResId, App.view);
|
||||
|
||||
Button btnDelete = (Button) (mView.findViewById(R.id.bubble_delete));
|
||||
btnDelete.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
removePoint(mSelectedPoint);
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ExtendedMarkerItem item) {
|
||||
mSelectedPoint = ((Integer) item.getRelatedObject()).intValue();
|
||||
super.onOpen(item);
|
||||
}
|
||||
}
|
||||
|
||||
class RouteBar {
|
||||
|
||||
TextView mDistance = null;
|
||||
TextView mRouteLength = null;
|
||||
TextView mTravelTime = null;
|
||||
ImageView mClearButton = null;
|
||||
RelativeLayout mRouteBarView = null;
|
||||
|
||||
RouteBar(Activity activity) {
|
||||
|
||||
mRouteBarView = (RelativeLayout) activity.findViewById(R.id.route_bar);
|
||||
mDistance = (TextView) activity.findViewById(R.id.route_bar_distance);
|
||||
mRouteLength = (TextView) activity.findViewById(R.id.route_bar_route_length);
|
||||
mTravelTime = (TextView) activity.findViewById(R.id.route_bar_travel_time);
|
||||
|
||||
mClearButton = (ImageView) activity.findViewById(R.id.route_bar_clear);
|
||||
|
||||
mRouteBarView.setVisibility(View.INVISIBLE);
|
||||
|
||||
mClearButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mRouteBarView.setVisibility(View.INVISIBLE);
|
||||
clearOverlays();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void set(Route result) {
|
||||
DecimalFormat twoDForm = new DecimalFormat("#.#");
|
||||
DecimalFormat oneDForm = new DecimalFormat("#");
|
||||
int hour = ((int) result.duration / 3600);
|
||||
int minute = ((int) result.duration % 3600) / 60;
|
||||
String time = "";
|
||||
if (hour == 0 && minute == 0) {
|
||||
time = "?";
|
||||
} else if (hour == 0 && minute != 0) {
|
||||
time = minute + "m";
|
||||
} else {
|
||||
time = hour + "h " + minute + "m";
|
||||
}
|
||||
|
||||
double dis = ((double) (mStartPoint.distanceTo(mDestinationPoint))) / 1000;
|
||||
String distance;
|
||||
String shortpath;
|
||||
if (dis < 100) {
|
||||
distance = twoDForm.format(dis);
|
||||
} else {
|
||||
distance = oneDForm.format(dis);
|
||||
}
|
||||
if (result.length == 0) {
|
||||
shortpath = "?";
|
||||
} else if (result.length < 100) {
|
||||
shortpath = twoDForm.format(result.length);
|
||||
} else {
|
||||
shortpath = oneDForm.format(result.length);
|
||||
}
|
||||
|
||||
mRouteBarView.setVisibility(View.VISIBLE);
|
||||
mDistance.setText(distance + " km");
|
||||
mTravelTime.setText(time);
|
||||
mRouteLength.setText(shortpath + " km");
|
||||
}
|
||||
}
|
||||
}
|
||||
518
vtm-app/src/org/oscim/app/TileMap.java
Executable file
518
vtm-app/src/org/oscim/app/TileMap.java
Executable file
@@ -0,0 +1,518 @@
|
||||
/* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.app;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.oscim.android.MapView;
|
||||
import org.oscim.app.location.Compass;
|
||||
import org.oscim.app.location.LocationDialog;
|
||||
import org.oscim.app.location.LocationHandler;
|
||||
import org.oscim.app.preferences.EditPreferences;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.overlay.DistanceTouchOverlay;
|
||||
import org.osmdroid.location.POI;
|
||||
import org.osmdroid.overlays.MapEventsReceiver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TileMap extends MapActivity implements MapEventsReceiver {
|
||||
final static Logger log = LoggerFactory.getLogger(TileMap.class);
|
||||
|
||||
private static final int DIALOG_ENTER_COORDINATES = 0;
|
||||
private static final int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
|
||||
|
||||
//private static final int SELECT_RENDER_THEME_FILE = 1;
|
||||
protected static final int POIS_REQUEST = 2;
|
||||
|
||||
private LocationHandler mLocation;
|
||||
private Compass mCompass;
|
||||
|
||||
private Menu mMenu = null;
|
||||
private MapLayers mMapLayers;
|
||||
|
||||
public MapLayers getMapLayers() {
|
||||
return mMapLayers;
|
||||
}
|
||||
|
||||
private DistanceTouchOverlay mDistanceTouch;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_tilemap);
|
||||
App.view = (MapView) findViewById(R.id.mapView);
|
||||
registerMapView(App.view);
|
||||
|
||||
App.map = mMap;
|
||||
App.activity = this;
|
||||
|
||||
mMapLayers = new MapLayers();
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mMapLayers.setBaseMap(prefs);
|
||||
|
||||
if (!prefs.contains("distanceTouch"))
|
||||
prefs.edit().putBoolean("distanceTouch", true).apply();
|
||||
|
||||
if (prefs.getBoolean("distanceTouch", true)) {
|
||||
mDistanceTouch = new DistanceTouchOverlay(mMap, this);
|
||||
mMap.layers().add(mDistanceTouch);
|
||||
}
|
||||
|
||||
mCompass = new Compass(this, mMap);
|
||||
mMap.layers().add(mCompass);
|
||||
|
||||
mLocation = new LocationHandler(this, mCompass);
|
||||
|
||||
App.poiSearch = new POISearch();
|
||||
App.routeSearch = new RouteSearch();
|
||||
|
||||
registerForContextMenu(App.view);
|
||||
|
||||
handleIntent(getIntent(), true);
|
||||
}
|
||||
|
||||
public Compass getCompass() {
|
||||
return mCompass;
|
||||
}
|
||||
|
||||
public LocationHandler getLocationHandler() {
|
||||
return mLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
handleIntent(intent, false);
|
||||
}
|
||||
|
||||
private void handleIntent(Intent intent, boolean start) {
|
||||
if (intent == null)
|
||||
return;
|
||||
|
||||
Uri uri = intent.getData();
|
||||
if (uri != null) {
|
||||
String scheme = uri.getSchemeSpecificPart();
|
||||
log.debug("got intent: " + (scheme == null ? "" : scheme));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.options_menu, menu);
|
||||
mMenu = menu;
|
||||
toggleMenuCheck();
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_info_about:
|
||||
startActivity(new Intent(this, InfoView.class));
|
||||
break;
|
||||
|
||||
case R.id.menu_position:
|
||||
break;
|
||||
|
||||
case R.id.menu_poi_nearby:
|
||||
Intent intent = new Intent(this, POIActivity.class);
|
||||
startActivityForResult(intent, TileMap.POIS_REQUEST);
|
||||
break;
|
||||
|
||||
case R.id.menu_compass_2d:
|
||||
if (!item.isChecked()) {
|
||||
// FIXME
|
||||
//mMapView.getMapViewPosition().setTilt(0);
|
||||
mCompass.setMode(Compass.Mode.C2D);
|
||||
} else {
|
||||
mCompass.setMode(Compass.Mode.OFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.menu_compass_3d:
|
||||
if (!item.isChecked()) {
|
||||
mCompass.setMode(Compass.Mode.C3D);
|
||||
} else {
|
||||
mCompass.setMode(Compass.Mode.OFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.menu_position_my_location_enable:
|
||||
if (!item.isChecked()) {
|
||||
mLocation.setMode(LocationHandler.Mode.SHOW);
|
||||
mLocation.setCenterOnFirstFix();
|
||||
} else {
|
||||
mLocation.setMode(LocationHandler.Mode.OFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.menu_position_follow_location:
|
||||
if (!item.isChecked()) {
|
||||
mLocation.setMode(LocationHandler.Mode.SNAP);
|
||||
} else {
|
||||
mLocation.setMode(LocationHandler.Mode.OFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case R.id.menu_layer_openstreetmap:
|
||||
case R.id.menu_layer_naturalearth:
|
||||
int bgId = item.getItemId();
|
||||
// toggle if already enabled
|
||||
if (bgId == mMapLayers.getBackgroundId())
|
||||
bgId = -1;
|
||||
|
||||
mMapLayers.setBackgroundMap(bgId);
|
||||
mMap.updateMap(true);
|
||||
break;
|
||||
|
||||
case R.id.menu_layer_grid:
|
||||
mMapLayers.enableGridOverlay(!mMapLayers.isGridEnabled());
|
||||
mMap.updateMap(true);
|
||||
break;
|
||||
|
||||
case R.id.menu_position_enter_coordinates:
|
||||
showDialog(DIALOG_ENTER_COORDINATES);
|
||||
break;
|
||||
|
||||
//case R.id.menu_position_map_center:
|
||||
// MapPosition mapCenter = mBaseLayer.getMapFileCenter();
|
||||
// if (mapCenter != null)
|
||||
// mMap.setCenter(mapCenter.getGeoPoint());
|
||||
// break;
|
||||
|
||||
case R.id.menu_preferences:
|
||||
startActivity(new Intent(this, EditPreferences.class));
|
||||
overridePendingTransition(R.anim.slide_right, R.anim.slide_left2);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleMenuCheck();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void toggleMenuCheck() {
|
||||
|
||||
mMenu.findItem(R.id.menu_compass_2d)
|
||||
.setChecked(mCompass.getMode() == Compass.Mode.C2D);
|
||||
mMenu.findItem(R.id.menu_compass_3d)
|
||||
.setChecked(mCompass.getMode() == Compass.Mode.C3D);
|
||||
|
||||
mMenu.findItem(R.id.menu_position_my_location_enable)
|
||||
.setChecked(mLocation.getMode() == LocationHandler.Mode.SHOW);
|
||||
mMenu.findItem(R.id.menu_position_follow_location)
|
||||
.setChecked(mLocation.getMode() == LocationHandler.Mode.SNAP);
|
||||
|
||||
int bgId = mMapLayers.getBackgroundId();
|
||||
mMenu.findItem(R.id.menu_layer_naturalearth)
|
||||
.setChecked(bgId == R.id.menu_layer_naturalearth);
|
||||
|
||||
mMenu.findItem(R.id.menu_layer_openstreetmap)
|
||||
.setChecked(bgId == R.id.menu_layer_openstreetmap);
|
||||
|
||||
mMenu.findItem(R.id.menu_layer_grid)
|
||||
.setChecked(mMapLayers.isGridEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
|
||||
if (!isPreHoneyComb()) {
|
||||
menu.clear();
|
||||
onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
menu.findItem(R.id.menu_position_map_center).setVisible(false);
|
||||
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
switch (requestCode) {
|
||||
case POIS_REQUEST:
|
||||
log.debug("result: POIS_REQUEST");
|
||||
if (resultCode == RESULT_OK) {
|
||||
int id = intent.getIntExtra("ID", 0);
|
||||
log.debug("result: POIS_REQUEST: " + id);
|
||||
|
||||
App.poiSearch.poiMarkers.showBubbleOnItem(id);
|
||||
POI poi = App.poiSearch.getPOIs().get(id);
|
||||
|
||||
if (poi.bbox != null)
|
||||
mMap.animator().animateTo(poi.bbox);
|
||||
else
|
||||
mMap.animator().animateTo(poi.location);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isPreHoneyComb() {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
if (id == DIALOG_ENTER_COORDINATES) {
|
||||
if (mLocationDialog == null)
|
||||
mLocationDialog = new LocationDialog();
|
||||
|
||||
return mLocationDialog.createDialog(this);
|
||||
|
||||
} else if (id == DIALOG_LOCATION_PROVIDER_DISABLED) {
|
||||
builder.setIcon(android.R.drawable.ic_menu_info_details);
|
||||
builder.setTitle(R.string.error);
|
||||
builder.setMessage(R.string.no_location_provider_available);
|
||||
builder.setPositiveButton(R.string.ok, null);
|
||||
return builder.create();
|
||||
} else {
|
||||
// no dialog will be created
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mCompass.pause();
|
||||
mLocation.pause();
|
||||
}
|
||||
|
||||
LocationDialog mLocationDialog;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, final Dialog dialog) {
|
||||
if (id == DIALOG_ENTER_COORDINATES) {
|
||||
|
||||
mLocationDialog.prepareDialog(mMap, dialog);
|
||||
|
||||
} else {
|
||||
super.onPrepareDialog(id, dialog);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
mCompass.resume();
|
||||
mLocation.resume();
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mMapLayers.setPreferences(preferences);
|
||||
|
||||
if (preferences.getBoolean("fullscreen", false)) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
}
|
||||
|
||||
App.lockOrientation(this);
|
||||
|
||||
boolean distanceTouch = preferences.getBoolean("distanceTouch", true);
|
||||
if (distanceTouch) {
|
||||
if (mDistanceTouch == null) {
|
||||
mDistanceTouch = new DistanceTouchOverlay(mMap, this);
|
||||
mMap.layers().add(mDistanceTouch);
|
||||
}
|
||||
} else {
|
||||
mMap.layers().remove(mDistanceTouch);
|
||||
mDistanceTouch = null;
|
||||
}
|
||||
|
||||
mMap.updateMap(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the UI thread to display the given text message as toast
|
||||
* notification.
|
||||
*
|
||||
* @param text the text message to display
|
||||
*/
|
||||
public void showToastOnUiThread(final String text) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast toast = Toast.makeText(TileMap.this, text, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private enum Mode {
|
||||
DEFAULT,
|
||||
SHOW_LOCATION,
|
||||
SNAP_LOCATION,
|
||||
COMPASS_2D,
|
||||
COMPASS_3D,
|
||||
}
|
||||
|
||||
private int mMapMode = 0;
|
||||
|
||||
public void toggleLocation(View V) {
|
||||
|
||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
|
||||
|
||||
mMapMode += 1;
|
||||
mMapMode %= Mode.values().length;
|
||||
|
||||
setInteractionMode(mMapMode);
|
||||
}
|
||||
|
||||
private void setInteractionMode(int mapMode) {
|
||||
Mode m = Mode.values()[mapMode];
|
||||
|
||||
switch (m) {
|
||||
case DEFAULT:
|
||||
|
||||
mLocation.setMode(LocationHandler.Mode.OFF);
|
||||
mCompass.setMode(Compass.Mode.OFF);
|
||||
|
||||
App.activity.showToastOnUiThread("Manual");
|
||||
|
||||
break;
|
||||
case SHOW_LOCATION:
|
||||
mLocation.setMode(LocationHandler.Mode.SHOW);
|
||||
mCompass.setMode(Compass.Mode.OFF);
|
||||
App.activity.showToastOnUiThread(App.activity
|
||||
.getString(R.string.menu_position_my_location_enable));
|
||||
break;
|
||||
|
||||
case SNAP_LOCATION:
|
||||
mLocation.setMode(LocationHandler.Mode.SNAP);
|
||||
mCompass.setMode(Compass.Mode.OFF);
|
||||
App.activity.showToastOnUiThread(App.activity
|
||||
.getString(R.string.menu_position_follow_location));
|
||||
break;
|
||||
|
||||
case COMPASS_2D:
|
||||
// FIXME
|
||||
//mMapView.getMapViewPosition().setTilt(0);
|
||||
|
||||
mLocation.setMode(LocationHandler.Mode.SHOW);
|
||||
mCompass.setMode(Compass.Mode.C2D);
|
||||
App.activity.showToastOnUiThread("Compass 2D");
|
||||
break;
|
||||
|
||||
case COMPASS_3D:
|
||||
mLocation.setMode(LocationHandler.Mode.SHOW);
|
||||
mCompass.setMode(Compass.Mode.C3D);
|
||||
App.activity.showToastOnUiThread("Compass 3D");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
App.map.updateMap(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Context Menu when clicking on the {@link Map}
|
||||
*/
|
||||
private GeoPoint mLongPressGeoPoint;
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.map_menu, menu);
|
||||
|
||||
if (App.poiSearch.getPOIs().isEmpty())
|
||||
menu.removeItem(R.id.menu_poi_clear);
|
||||
|
||||
if (App.routeSearch.isEmpty())
|
||||
menu.removeItem(R.id.menu_route_clear);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
if (App.poiSearch.onContextItemSelected(item, mLongPressGeoPoint))
|
||||
return true;
|
||||
|
||||
if (App.routeSearch.onContextItemSelected(item, mLongPressGeoPoint))
|
||||
return true;
|
||||
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* MapEventsReceiver implementation
|
||||
*/
|
||||
@Override
|
||||
public boolean singleTapUpHelper(GeoPoint p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPressHelper(GeoPoint p) {
|
||||
mLongPressGeoPoint = p;
|
||||
openContextMenu(App.view);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean longPressHelper(final GeoPoint p1, final GeoPoint p2) {
|
||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
|
||||
showToastOnUiThread("Distance Touch!");
|
||||
App.routeSearch.showRoute(p1, p2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.filefilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
/**
|
||||
* Accepts all readable directories and all readable files with a given
|
||||
* extension.
|
||||
*/
|
||||
public class FilterByFileExtension implements FileFilter {
|
||||
private final String extension;
|
||||
|
||||
/**
|
||||
* @param extension the allowed file name extension.
|
||||
*/
|
||||
public FilterByFileExtension(String extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
// accept only readable files
|
||||
if (file.canRead()) {
|
||||
if (file.isDirectory()) {
|
||||
// accept all directories
|
||||
return true;
|
||||
} else if (file.isFile() && file.getName().endsWith(this.extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
29
vtm-app/src/org/oscim/app/filefilter/ValidFileFilter.java
Normal file
29
vtm-app/src/org/oscim/app/filefilter/ValidFileFilter.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.filefilter;
|
||||
|
||||
import org.oscim.tiling.TileSource.OpenResult;
|
||||
|
||||
import java.io.FileFilter;
|
||||
|
||||
/**
|
||||
* An extension of the {@link FileFilter} interface.
|
||||
*/
|
||||
public interface ValidFileFilter extends FileFilter {
|
||||
/**
|
||||
* @return the result of the last {@link #accept} call (might be null).
|
||||
*/
|
||||
OpenResult getFileOpenResult();
|
||||
}
|
||||
42
vtm-app/src/org/oscim/app/filefilter/ValidMapFile.java
Normal file
42
vtm-app/src/org/oscim/app/filefilter/ValidMapFile.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.filefilter;
|
||||
|
||||
import org.oscim.tiling.TileSource.OpenResult;
|
||||
import org.oscim.tiling.source.mapfile.MapFileTileSource;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Accepts all valid map files.
|
||||
*/
|
||||
public final class ValidMapFile implements ValidFileFilter {
|
||||
private OpenResult openResult;
|
||||
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
MapFileTileSource mapFileSource = new MapFileTileSource();
|
||||
mapFileSource.setMapFile(file.getAbsolutePath());
|
||||
|
||||
this.openResult = mapFileSource.open();
|
||||
mapFileSource.close();
|
||||
return this.openResult.isSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenResult getFileOpenResult() {
|
||||
return this.openResult;
|
||||
}
|
||||
}
|
||||
72
vtm-app/src/org/oscim/app/filefilter/ValidRenderTheme.java
Normal file
72
vtm-app/src/org/oscim/app/filefilter/ValidRenderTheme.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2016 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.filefilter;
|
||||
|
||||
import org.oscim.theme.XmlThemeBuilder;
|
||||
import org.oscim.tiling.TileSource.OpenResult;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* Accepts all valid render theme XML files.
|
||||
*/
|
||||
public final class ValidRenderTheme implements ValidFileFilter {
|
||||
private OpenResult openResult;
|
||||
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
InputStream inputStream = null;
|
||||
|
||||
try {
|
||||
inputStream = new FileInputStream(file);
|
||||
XmlThemeBuilder renderThemeHandler = new XmlThemeBuilder(file.getParent());
|
||||
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
|
||||
xmlReader.setContentHandler(renderThemeHandler);
|
||||
xmlReader.parse(new InputSource(inputStream));
|
||||
this.openResult = OpenResult.SUCCESS;
|
||||
} catch (ParserConfigurationException e) {
|
||||
this.openResult = new OpenResult(e.getMessage());
|
||||
} catch (SAXException e) {
|
||||
this.openResult = new OpenResult(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
this.openResult = new OpenResult(e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
this.openResult = new OpenResult(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return this.openResult.isSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenResult getFileOpenResult() {
|
||||
return this.openResult;
|
||||
}
|
||||
}
|
||||
262
vtm-app/src/org/oscim/app/filepicker/FilePicker.java
Executable file
262
vtm-app/src/org/oscim/app/filepicker/FilePicker.java
Executable file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.filepicker;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.GridView;
|
||||
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.app.filefilter.ValidFileFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* A FilePicker displays the contents of directories. The user can navigate
|
||||
* within the file system and select a single
|
||||
* file whose path is then returned to the calling activity. The ordering of
|
||||
* directory contents can be specified via
|
||||
* {@link #setFileComparator(Comparator)}. By default subfolders and files are
|
||||
* grouped and each group is ordered
|
||||
* alphabetically.
|
||||
* <p/>
|
||||
* A {@link FileFilter} can be activated via
|
||||
* {@link #setFileDisplayFilter(FileFilter)} to restrict the displayed files and
|
||||
* folders. By default all files and folders are visible.
|
||||
* <p/>
|
||||
* Another <code>FileFilter</code> can be applied via
|
||||
* {@link #setFileSelectFilter(ValidFileFilter)} to check if a selected file is
|
||||
* valid before its path is returned. By default all files are considered as
|
||||
* valid and can be selected.
|
||||
*/
|
||||
public class FilePicker extends Activity implements AdapterView.OnItemClickListener {
|
||||
/**
|
||||
* The name of the extra data in the result {@link Intent}.
|
||||
*/
|
||||
public static final String SELECTED_FILE = "selectedFile";
|
||||
|
||||
private static final String CURRENT_DIRECTORY = "currentDirectory";
|
||||
private static final String DEFAULT_DIRECTORY = "/";
|
||||
private static final int DIALOG_FILE_INVALID = 0;
|
||||
// private static final int DIALOG_FILE_SELECT = 1;
|
||||
private static Comparator<File> fileComparator = getDefaultFileComparator();
|
||||
private static FileFilter fileDisplayFilter;
|
||||
private static ValidFileFilter fileSelectFilter;
|
||||
private static final String PREFERENCES_FILE = "FilePicker";
|
||||
|
||||
/**
|
||||
* Sets the file comparator which is used to order the contents of all
|
||||
* directories before displaying them. If set to
|
||||
* null, subfolders and files will not be ordered.
|
||||
*
|
||||
* @param fileComparator the file comparator (may be null).
|
||||
*/
|
||||
public static void setFileComparator(Comparator<File> fileComparator) {
|
||||
FilePicker.fileComparator = fileComparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file display filter. This filter is used to determine which
|
||||
* files and subfolders of directories will be
|
||||
* displayed. If set to null, all files and subfolders are shown.
|
||||
*
|
||||
* @param fileDisplayFilter the file display filter (may be null).
|
||||
*/
|
||||
public static void setFileDisplayFilter(FileFilter fileDisplayFilter) {
|
||||
FilePicker.fileDisplayFilter = fileDisplayFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file select filter. This filter is used when the user selects a
|
||||
* file to determine if it is valid. If set
|
||||
* to null, all files are considered as valid.
|
||||
*
|
||||
* @param fileSelectFilter the file selection filter (may be null).
|
||||
*/
|
||||
public static void setFileSelectFilter(ValidFileFilter fileSelectFilter) {
|
||||
FilePicker.fileSelectFilter = fileSelectFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the default file comparator.
|
||||
*
|
||||
* @return the default file comparator.
|
||||
*/
|
||||
private static Comparator<File> getDefaultFileComparator() {
|
||||
// order all files by type and alphabetically by name
|
||||
return new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File file1, File file2) {
|
||||
if (file1.isDirectory() && !file2.isDirectory()) {
|
||||
return -1;
|
||||
} else if (!file1.isDirectory() && file2.isDirectory()) {
|
||||
return 1;
|
||||
} else {
|
||||
return file1.getName().compareToIgnoreCase(file2.getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private File currentDirectory;
|
||||
private FilePickerIconAdapter filePickerIconAdapter;
|
||||
private File[] files;
|
||||
private File[] filesWithParentFolder;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
File selectedFile = this.files[(int) id];
|
||||
if (selectedFile.isDirectory()) {
|
||||
this.currentDirectory = selectedFile;
|
||||
browseToCurrentDirectory();
|
||||
} else if (fileSelectFilter == null || fileSelectFilter.accept(selectedFile)) {
|
||||
setResult(RESULT_OK,
|
||||
new Intent().putExtra(SELECTED_FILE, selectedFile.getAbsolutePath()));
|
||||
finish();
|
||||
} else {
|
||||
showDialog(DIALOG_FILE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Browses to the current directory.
|
||||
*/
|
||||
private void browseToCurrentDirectory() {
|
||||
setTitle(this.currentDirectory.getAbsolutePath());
|
||||
|
||||
// read the subfolders and files from the current directory
|
||||
if (fileDisplayFilter == null) {
|
||||
this.files = this.currentDirectory.listFiles();
|
||||
} else {
|
||||
this.files = this.currentDirectory.listFiles(fileDisplayFilter);
|
||||
}
|
||||
|
||||
if (this.files == null) {
|
||||
this.files = new File[0];
|
||||
} else {
|
||||
// order the subfolders and files
|
||||
Arrays.sort(this.files, fileComparator);
|
||||
}
|
||||
|
||||
// if a parent directory exists, add it at the first position
|
||||
if (this.currentDirectory.getParentFile() != null) {
|
||||
this.filesWithParentFolder = new File[this.files.length + 1];
|
||||
this.filesWithParentFolder[0] = this.currentDirectory.getParentFile();
|
||||
System.arraycopy(this.files, 0, this.filesWithParentFolder, 1,
|
||||
this.files.length);
|
||||
this.files = this.filesWithParentFolder;
|
||||
this.filePickerIconAdapter.setFiles(this.files, true);
|
||||
} else {
|
||||
this.filePickerIconAdapter.setFiles(this.files, false);
|
||||
}
|
||||
this.filePickerIconAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_file_picker);
|
||||
|
||||
this.filePickerIconAdapter = new FilePickerIconAdapter(this);
|
||||
GridView gridView = (GridView) findViewById(R.id.filePickerView);
|
||||
gridView.setOnItemClickListener(this);
|
||||
gridView.setAdapter(this.filePickerIconAdapter);
|
||||
|
||||
// if (savedInstanceState == null) {
|
||||
// // first start of this instance
|
||||
// showDialog(DIALOG_FILE_SELECT);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
switch (id) {
|
||||
case DIALOG_FILE_INVALID:
|
||||
builder.setIcon(android.R.drawable.ic_menu_info_details);
|
||||
builder.setTitle(R.string.error);
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(getString(R.string.file_invalid));
|
||||
stringBuilder.append("\n\n");
|
||||
stringBuilder.append(FilePicker.fileSelectFilter.getFileOpenResult()
|
||||
.getErrorMessage());
|
||||
|
||||
builder.setMessage(stringBuilder.toString());
|
||||
builder.setPositiveButton(R.string.ok, null);
|
||||
return builder.create();
|
||||
// case DIALOG_FILE_SELECT:
|
||||
// builder.setMessage(R.string.file_select);
|
||||
// builder.setPositiveButton(R.string.ok, null);
|
||||
// return builder.create();
|
||||
default:
|
||||
// do dialog will be created
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
// save the current directory
|
||||
Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit();
|
||||
editor.clear();
|
||||
if (this.currentDirectory != null) {
|
||||
editor.putString(CURRENT_DIRECTORY, this.currentDirectory.getAbsolutePath());
|
||||
}
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@TargetApi(11)
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||
getActionBar().hide();
|
||||
|
||||
// check if the full screen mode should be activated
|
||||
// if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen",
|
||||
// false)) {
|
||||
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// } else {
|
||||
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// }
|
||||
|
||||
// restore the current directory
|
||||
SharedPreferences preferences = getSharedPreferences(PREFERENCES_FILE,
|
||||
MODE_PRIVATE);
|
||||
this.currentDirectory = new File(preferences.getString(CURRENT_DIRECTORY,
|
||||
DEFAULT_DIRECTORY));
|
||||
if (!this.currentDirectory.exists() || !this.currentDirectory.canRead()) {
|
||||
this.currentDirectory = new File(DEFAULT_DIRECTORY);
|
||||
}
|
||||
browseToCurrentDirectory();
|
||||
}
|
||||
}
|
||||
114
vtm-app/src/org/oscim/app/filepicker/FilePickerIconAdapter.java
Executable file
114
vtm-app/src/org/oscim/app/filepicker/FilePickerIconAdapter.java
Executable file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.filepicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.oscim.app.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An adapter for the FilePicker GridView.
|
||||
*/
|
||||
class FilePickerIconAdapter extends BaseAdapter {
|
||||
private final Context context;
|
||||
private File currentFile;
|
||||
private File[] files;
|
||||
private boolean hasParentFolder;
|
||||
private TextView textView;
|
||||
|
||||
/**
|
||||
* Creates a new FilePickerIconAdapter with the given context.
|
||||
*
|
||||
* @param context the context of this adapter, through which new Views are
|
||||
* created.
|
||||
*/
|
||||
FilePickerIconAdapter(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (this.files == null) {
|
||||
return 0;
|
||||
}
|
||||
return this.files.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int index) {
|
||||
return this.files[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int index, View convertView, ViewGroup parent) {
|
||||
if (convertView instanceof TextView) {
|
||||
// recycle the old view
|
||||
this.textView = (TextView) convertView;
|
||||
} else {
|
||||
// create a new view object
|
||||
this.textView = new TextView(this.context);
|
||||
this.textView.setLines(2);
|
||||
this.textView.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
this.textView.setPadding(5, 10, 5, 10);
|
||||
}
|
||||
|
||||
if (index == 0 && this.hasParentFolder) {
|
||||
// the parent directory of the current folder
|
||||
this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
|
||||
R.drawable.file_picker_back, 0, 0);
|
||||
this.textView.setText("..");
|
||||
} else {
|
||||
this.currentFile = this.files[index];
|
||||
if (this.currentFile.isDirectory()) {
|
||||
this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
|
||||
R.drawable.file_picker_folder,
|
||||
0,
|
||||
0);
|
||||
} else {
|
||||
this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
|
||||
R.drawable.file_picker_file,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
this.textView.setText(this.currentFile.getName());
|
||||
}
|
||||
return this.textView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data of this adapter.
|
||||
*
|
||||
* @param files the new files for this adapter.
|
||||
* @param newHasParentFolder true if the file array has a parent folder at index 0, false
|
||||
* otherwise.
|
||||
*/
|
||||
void setFiles(File[] files, boolean newHasParentFolder) {
|
||||
this.files = files.clone();
|
||||
this.hasParentFolder = newHasParentFolder;
|
||||
}
|
||||
}
|
||||
392
vtm-app/src/org/oscim/app/location/Compass.java
Normal file
392
vtm-app/src/org/oscim/app/location/Compass.java
Normal file
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright 2013 Ahmad Saleem
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.app.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.map.Map;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Compass extends Layer implements SensorEventListener,
|
||||
Map.UpdateListener {
|
||||
|
||||
// final static Logger log = LoggerFactory.getLogger(Compass.class);
|
||||
|
||||
public enum Mode {
|
||||
OFF, C2D, C3D,
|
||||
}
|
||||
|
||||
private final SensorManager mSensorManager;
|
||||
private final ImageView mArrowView;
|
||||
|
||||
// private final float[] mRotationM = new float[9];
|
||||
private final float[] mRotationV = new float[3];
|
||||
|
||||
// private float[] mAccelV = new float[3];
|
||||
// private float[] mMagnetV = new float[3];
|
||||
// private boolean mLastAccelerometerSet;
|
||||
// private boolean mLastMagnetometerSet;
|
||||
|
||||
private float mCurRotation;
|
||||
private float mCurTilt;
|
||||
|
||||
private boolean mControlOrientation;
|
||||
|
||||
private Mode mMode = Mode.OFF;
|
||||
private int mListeners;
|
||||
|
||||
@Override
|
||||
public void onMapEvent(Event e, MapPosition mapPosition) {
|
||||
if (!mControlOrientation) {
|
||||
float rotation = -mapPosition.bearing;
|
||||
adjustArrow(rotation, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public Compass(Context context, Map map) {
|
||||
super(map);
|
||||
|
||||
mSensorManager = (SensorManager) context
|
||||
.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
// List<Sensor> s = mSensorManager.getSensorList(Sensor.TYPE_ALL);
|
||||
// for (Sensor sensor : s)
|
||||
// log.debug(sensor.toString());
|
||||
|
||||
mArrowView = (ImageView) App.activity.findViewById(R.id.compass);
|
||||
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
public synchronized float getRotation() {
|
||||
return mCurRotation;
|
||||
}
|
||||
|
||||
public void controlView(boolean enable) {
|
||||
mControlOrientation = enable;
|
||||
}
|
||||
|
||||
public boolean controlView() {
|
||||
return mControlOrientation;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
if (mode == mMode)
|
||||
return;
|
||||
|
||||
if (mode == Mode.OFF) {
|
||||
setEnabled(false);
|
||||
|
||||
mMap.getEventLayer().enableRotation(true);
|
||||
mMap.getEventLayer().enableTilt(true);
|
||||
} else if (mMode == Mode.OFF) {
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
if (mode == Mode.C3D) {
|
||||
mMap.getEventLayer().enableRotation(false);
|
||||
mMap.getEventLayer().enableTilt(false);
|
||||
} else if (mode == Mode.C2D) {
|
||||
mMap.getEventLayer().enableRotation(false);
|
||||
mMap.getEventLayer().enableTilt(true);
|
||||
}
|
||||
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
mListeners += enabled ? 1 : -1;
|
||||
|
||||
if (mListeners == 1) {
|
||||
resume();
|
||||
} else if (mListeners == 0) {
|
||||
pause();
|
||||
|
||||
} else if (mListeners < 0) {
|
||||
// then bad
|
||||
mListeners = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (mListeners <= 0)
|
||||
return;
|
||||
|
||||
super.setEnabled(true);
|
||||
|
||||
Sensor sensor;
|
||||
// Sensor sensor =
|
||||
// mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
|
||||
// Sensor sensor =
|
||||
// mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
// sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
|
||||
// mSensorManager.registerListener(this, sensor,
|
||||
// SensorManager.SENSOR_DELAY_UI);
|
||||
// sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
|
||||
// mSensorManager.registerListener(this, sensor,
|
||||
// SensorManager.SENSOR_DELAY_UI);
|
||||
|
||||
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
|
||||
mSensorManager.registerListener(this, sensor,
|
||||
SensorManager.SENSOR_DELAY_UI);
|
||||
|
||||
// mLastAccelerometerSet = false;
|
||||
// mLastMagnetometerSet = false;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (mListeners <= 0)
|
||||
return;
|
||||
|
||||
super.setEnabled(false);
|
||||
mSensorManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
public void adjustArrow(float prev, float cur) {
|
||||
Animation an = new RotateAnimation(-prev,
|
||||
-cur,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f);
|
||||
|
||||
an.setDuration(100);
|
||||
an.setRepeatCount(0);
|
||||
an.setFillAfter(true);
|
||||
|
||||
mArrowView.startAnimation(an);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
|
||||
if (event.sensor.getType() != Sensor.TYPE_ORIENTATION)
|
||||
return;
|
||||
System.arraycopy(event.values, 0, mRotationV, 0, event.values.length);
|
||||
|
||||
// SensorManager.getRotationMatrixFromVector(mRotationM, event.values);
|
||||
// SensorManager.getOrientation(mRotationM, mRotationV);
|
||||
|
||||
// int type = event.sensor.getType();
|
||||
// if (type == Sensor.TYPE_ACCELEROMETER) {
|
||||
// System.arraycopy(event.values, 0, mAccelV, 0, event.values.length);
|
||||
// mLastAccelerometerSet = true;
|
||||
// } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
|
||||
// System.arraycopy(event.values, 0, mMagnetV, 0, event.values.length);
|
||||
// mLastMagnetometerSet = true;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// if (!mLastAccelerometerSet || !mLastMagnetometerSet)
|
||||
// return;
|
||||
|
||||
// SensorManager.getRotationMatrix(mRotationM, null, mAccelV, mMagnetV);
|
||||
// SensorManager.getOrientation(mRotationM, mRotationV);
|
||||
|
||||
// float rotation = (float) Math.toDegrees(mRotationV[0]);
|
||||
float rotation = mRotationV[0];
|
||||
|
||||
// handle(event);
|
||||
// if (!mOrientationOK)
|
||||
// return;
|
||||
// float rotation = (float) Math.toDegrees(mAzimuthRadians);
|
||||
|
||||
float change = rotation - mCurRotation;
|
||||
if (change > 180)
|
||||
change -= 360;
|
||||
else if (change < -180)
|
||||
change += 360;
|
||||
|
||||
// low-pass
|
||||
change *= 0.05;
|
||||
|
||||
rotation = mCurRotation + change;
|
||||
|
||||
if (rotation > 180)
|
||||
rotation -= 360;
|
||||
else if (rotation < -180)
|
||||
rotation += 360;
|
||||
|
||||
// float tilt = (float) Math.toDegrees(mRotationV[1]);
|
||||
// float tilt = (float) Math.toDegrees(mPitchAxisRadians);
|
||||
float tilt = mRotationV[1];
|
||||
|
||||
mCurTilt = mCurTilt + 0.2f * (tilt - mCurTilt);
|
||||
|
||||
if (mMode != Mode.OFF) {
|
||||
boolean redraw = false;
|
||||
|
||||
if (Math.abs(change) > 0.01) {
|
||||
adjustArrow(mCurRotation, rotation);
|
||||
mMap.viewport().setRotation(-rotation);
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (mMode == Mode.C3D)
|
||||
redraw |= mMap.viewport().setTilt(-mCurTilt * 1.5f);
|
||||
|
||||
if (redraw)
|
||||
mMap.updateMap(true);
|
||||
}
|
||||
mCurRotation = rotation;
|
||||
}
|
||||
|
||||
// from http://stackoverflow.com/questions/16317599/android-compass-that-
|
||||
// can-compensate-for-tilt-and-pitch/16386066#16386066
|
||||
|
||||
// private int mGravityAccuracy;
|
||||
// private int mMagneticFieldAccuracy;
|
||||
|
||||
// private float[] mGravityV = new float[3];
|
||||
// private float[] mMagFieldV = new float[3];
|
||||
// private float[] mEastV = new float[3];
|
||||
// private float[] mNorthV = new float[3];
|
||||
//
|
||||
// private float mNormGravity;
|
||||
// private float mNormMagField;
|
||||
//
|
||||
// private boolean mOrientationOK;
|
||||
// private float mAzimuthRadians;
|
||||
// private float mPitchRadians;
|
||||
// private float mPitchAxisRadians;
|
||||
//
|
||||
// private void handle(SensorEvent event) {
|
||||
// int SensorType = event.sensor.getType();
|
||||
// switch (SensorType) {
|
||||
// case Sensor.TYPE_GRAVITY:
|
||||
// mLastAccelerometerSet = true;
|
||||
// System.arraycopy(event.values, 0, mGravityV, 0, mGravityV.length);
|
||||
// mNormGravity = (float) Math.sqrt(mGravityV[0] * mGravityV[0]
|
||||
// + mGravityV[1] * mGravityV[1] + mGravityV[2]
|
||||
// * mGravityV[2]);
|
||||
// for (int i = 0; i < mGravityV.length; i++)
|
||||
// mGravityV[i] /= mNormGravity;
|
||||
// break;
|
||||
// case Sensor.TYPE_MAGNETIC_FIELD:
|
||||
// mLastMagnetometerSet = true;
|
||||
// System.arraycopy(event.values, 0, mMagFieldV, 0, mMagFieldV.length);
|
||||
// mNormMagField = (float) Math.sqrt(mMagFieldV[0] * mMagFieldV[0]
|
||||
// + mMagFieldV[1] * mMagFieldV[1] + mMagFieldV[2]
|
||||
// * mMagFieldV[2]);
|
||||
// for (int i = 0; i < mMagFieldV.length; i++)
|
||||
// mMagFieldV[i] /= mNormMagField;
|
||||
// break;
|
||||
// }
|
||||
// if (!mLastAccelerometerSet || !mLastMagnetometerSet)
|
||||
// return;
|
||||
//
|
||||
// // first calculate the horizontal vector that points due east
|
||||
// float ex = mMagFieldV[1] * mGravityV[2] - mMagFieldV[2] * mGravityV[1];
|
||||
// float ey = mMagFieldV[2] * mGravityV[0] - mMagFieldV[0] * mGravityV[2];
|
||||
// float ez = mMagFieldV[0] * mGravityV[1] - mMagFieldV[1] * mGravityV[0];
|
||||
// float normEast = (float) Math.sqrt(ex * ex + ey * ey + ez * ez);
|
||||
//
|
||||
// if (mNormGravity * mNormMagField * normEast < 0.1f) { // Typical values
|
||||
// are > 100.
|
||||
// // device is close to free fall (or in space?), or close to magnetic
|
||||
// north pole.
|
||||
// mOrientationOK = false;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// mEastV[0] = ex / normEast;
|
||||
// mEastV[1] = ey / normEast;
|
||||
// mEastV[2] = ez / normEast;
|
||||
//
|
||||
// // next calculate the horizontal vector that points due north
|
||||
// float mdotG = (mGravityV[0] * mMagFieldV[0]
|
||||
// + mGravityV[1] * mMagFieldV[1]
|
||||
// + mGravityV[2] * mMagFieldV[2]);
|
||||
//
|
||||
// float nx = mMagFieldV[0] - mGravityV[0] * mdotG;
|
||||
// float ny = mMagFieldV[1] - mGravityV[1] * mdotG;
|
||||
// float nz = mMagFieldV[2] - mGravityV[2] * mdotG;
|
||||
// float normNorth = (float) Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
//
|
||||
// mNorthV[0] = nx / normNorth;
|
||||
// mNorthV[1] = ny / normNorth;
|
||||
// mNorthV[2] = nz / normNorth;
|
||||
//
|
||||
// // take account of screen rotation away from its natural rotation
|
||||
// //int rotation =
|
||||
// App.activity.getWindowManager().getDefaultDisplay().getRotation();
|
||||
// float screenDirection = 0;
|
||||
// //switch(rotation) {
|
||||
// // case Surface.ROTATION_0: screenDirection = 0; break;
|
||||
// // case Surface.ROTATION_90: screenDirection = (float)Math.PI/2; break;
|
||||
// // case Surface.ROTATION_180: screenDirection = (float)Math.PI; break;
|
||||
// // case Surface.ROTATION_270: screenDirection = 3*(float)Math.PI/2;
|
||||
// break;
|
||||
// //}
|
||||
// // NB: the rotation matrix has now effectively been calculated. It
|
||||
// consists of
|
||||
// // the three vectors mEastV[], mNorthV[] and mGravityV[]
|
||||
//
|
||||
// // calculate all the required angles from the rotation matrix
|
||||
// // NB: see
|
||||
// http://math.stackexchange.com/questions/381649/whats-the-best-3d-angular-
|
||||
// // co-ordinate-system-for-working-with-smartfone-apps
|
||||
// float sin = mEastV[1] - mNorthV[0], cos = mEastV[0] + mNorthV[1];
|
||||
// mAzimuthRadians = (float) (sin != 0 && cos != 0 ? Math.atan2(sin, cos) :
|
||||
// 0);
|
||||
// mPitchRadians = (float) Math.acos(mGravityV[2]);
|
||||
//
|
||||
// sin = -mEastV[1] - mNorthV[0];
|
||||
// cos = mEastV[0] - mNorthV[1];
|
||||
//
|
||||
// float aximuthPlusTwoPitchAxisRadians =
|
||||
// (float) (sin != 0 && cos != 0 ? Math.atan2(sin, cos) : 0);
|
||||
//
|
||||
// mPitchAxisRadians = (float) (aximuthPlusTwoPitchAxisRadians -
|
||||
// mAzimuthRadians) / 2;
|
||||
// mAzimuthRadians += screenDirection;
|
||||
// mPitchAxisRadians += screenDirection;
|
||||
//
|
||||
// mOrientationOK = true;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// int type = sensor.getType();
|
||||
// switch (type) {
|
||||
// case Sensor.TYPE_GRAVITY:
|
||||
// mGravityAccuracy = accuracy;
|
||||
// break;
|
||||
// case Sensor.TYPE_MAGNETIC_FIELD:
|
||||
// mMagneticFieldAccuracy = accuracy;
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
120
vtm-app/src/org/oscim/app/location/LocationDialog.java
Normal file
120
vtm-app/src/org/oscim/app/location/LocationDialog.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
* Copyright 2016 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.location;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.app.TileMap;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.map.Map;
|
||||
|
||||
public class LocationDialog {
|
||||
|
||||
public void prepareDialog(Map map, final Dialog dialog) {
|
||||
EditText editText = (EditText) dialog.findViewById(R.id.latitude);
|
||||
|
||||
MapPosition mapCenter = map.getMapPosition();
|
||||
|
||||
editText.setText(Double.toString(mapCenter.getLatitude()));
|
||||
|
||||
editText = (EditText) dialog.findViewById(R.id.longitude);
|
||||
editText.setText(Double.toString(mapCenter.getLongitude()));
|
||||
|
||||
SeekBar zoomlevel = (SeekBar) dialog.findViewById(R.id.zoomLevel);
|
||||
zoomlevel.setMax(20);
|
||||
zoomlevel.setProgress(10);
|
||||
|
||||
final TextView textView = (TextView) dialog.findViewById(R.id.zoomlevelValue);
|
||||
textView.setText(String.valueOf(zoomlevel.getProgress()));
|
||||
zoomlevel.setOnSeekBarChangeListener(new SeekBarChangeListener(textView));
|
||||
}
|
||||
|
||||
public Dialog createDialog(final TileMap map) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(map);
|
||||
builder.setIcon(android.R.drawable.ic_menu_mylocation);
|
||||
builder.setTitle(R.string.menu_position_enter_coordinates);
|
||||
LayoutInflater factory = LayoutInflater.from(map);
|
||||
final View view = factory.inflate(R.layout.dialog_enter_coordinates, null);
|
||||
builder.setView(view);
|
||||
|
||||
builder.setPositiveButton(R.string.go_to_position,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// disable GPS follow mode if it is enabled
|
||||
//map.mLocation.disableSnapToLocation();
|
||||
if (map.getLocationHandler().getMode() == LocationHandler.Mode.SNAP)
|
||||
map.getLocationHandler()
|
||||
.setMode(LocationHandler.Mode.SHOW);
|
||||
|
||||
// set the map center and zoom level
|
||||
EditText latitudeView = (EditText) view
|
||||
.findViewById(R.id.latitude);
|
||||
EditText longitudeView = (EditText) view
|
||||
.findViewById(R.id.longitude);
|
||||
double latitude = Double.parseDouble(latitudeView.getText()
|
||||
.toString());
|
||||
double longitude = Double.parseDouble(longitudeView.getText()
|
||||
.toString());
|
||||
|
||||
SeekBar zoomLevelView = (SeekBar) view
|
||||
.findViewById(R.id.zoomLevel);
|
||||
|
||||
int zoom = zoomLevelView.getProgress();
|
||||
|
||||
MapPosition mapPosition = new MapPosition();
|
||||
mapPosition.setPosition(latitude, longitude);
|
||||
mapPosition.setZoomLevel(zoom);
|
||||
App.map.setMapPosition(mapPosition);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
private final TextView textView;
|
||||
|
||||
SeekBarChangeListener(TextView textView) {
|
||||
this.textView = textView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
this.textView.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
236
vtm-app/src/org/oscim/app/location/LocationHandler.java
Normal file
236
vtm-app/src/org/oscim/app/location/LocationHandler.java
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2013 Ahmad Al-saleem
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.app.TileMap;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LocationHandler implements LocationListener {
|
||||
final static Logger log = LoggerFactory.getLogger(LocationHandler.class);
|
||||
|
||||
public enum Mode {
|
||||
OFF,
|
||||
SHOW,
|
||||
SNAP,
|
||||
}
|
||||
|
||||
private final static int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
|
||||
private final static int SHOW_LOCATION_ZOOM = 14;
|
||||
|
||||
private final LocationManager mLocationManager;
|
||||
private final LocationOverlay mLocationOverlay;
|
||||
|
||||
private Mode mMode = Mode.OFF;
|
||||
|
||||
private boolean mSetCenter;
|
||||
private MapPosition mMapPosition;
|
||||
|
||||
public LocationHandler(TileMap tileMap, Compass compass) {
|
||||
mLocationManager = (LocationManager) tileMap
|
||||
.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
mLocationOverlay = new LocationOverlay(App.map, compass);
|
||||
|
||||
mMapPosition = new MapPosition();
|
||||
}
|
||||
|
||||
public boolean setMode(Mode mode) {
|
||||
if (mode == mMode)
|
||||
return true;
|
||||
|
||||
if (mode == Mode.OFF) {
|
||||
disableShowMyLocation();
|
||||
|
||||
if (mMode == Mode.SNAP)
|
||||
App.map.getEventLayer().enableMove(true);
|
||||
}
|
||||
|
||||
if (mMode == Mode.OFF) {
|
||||
if (!enableShowMyLocation())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode == Mode.SNAP) {
|
||||
App.map.getEventLayer().enableMove(false);
|
||||
gotoLastKnownPosition();
|
||||
} else {
|
||||
App.map.getEventLayer().enableMove(true);
|
||||
}
|
||||
|
||||
// FIXME?
|
||||
mSetCenter = false;
|
||||
mMode = mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mMode;
|
||||
}
|
||||
|
||||
public boolean isFirstCenter() {
|
||||
return mSetCenter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean enableShowMyLocation() {
|
||||
|
||||
Criteria criteria = new Criteria();
|
||||
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||
String bestProvider = mLocationManager.getBestProvider(criteria, true);
|
||||
|
||||
if (bestProvider == null) {
|
||||
App.activity.showDialog(DIALOG_LOCATION_PROVIDER_DISABLED);
|
||||
return false;
|
||||
}
|
||||
|
||||
mLocationManager.requestLocationUpdates(bestProvider, 10000, 10, this);
|
||||
|
||||
Location location = gotoLastKnownPosition();
|
||||
if (location == null)
|
||||
return false;
|
||||
|
||||
mLocationOverlay.setEnabled(true);
|
||||
mLocationOverlay.setPosition(location.getLatitude(),
|
||||
location.getLongitude(),
|
||||
location.getAccuracy());
|
||||
|
||||
// FIXME -> implement LayerGroup
|
||||
App.map.layers().add(4, mLocationOverlay);
|
||||
|
||||
App.map.updateMap(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable "show my location" mode.
|
||||
*/
|
||||
private boolean disableShowMyLocation() {
|
||||
|
||||
mLocationManager.removeUpdates(this);
|
||||
mLocationOverlay.setEnabled(false);
|
||||
|
||||
App.map.layers().remove(mLocationOverlay);
|
||||
App.map.updateMap(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Location gotoLastKnownPosition() {
|
||||
Location location = null;
|
||||
float bestAccuracy = Float.MAX_VALUE;
|
||||
|
||||
for (String provider : mLocationManager.getProviders(true)) {
|
||||
Location l = mLocationManager.getLastKnownLocation(provider);
|
||||
if (l == null)
|
||||
continue;
|
||||
|
||||
float accuracy = l.getAccuracy();
|
||||
if (accuracy <= 0)
|
||||
accuracy = Float.MAX_VALUE;
|
||||
|
||||
if (location == null || accuracy <= bestAccuracy) {
|
||||
location = l;
|
||||
bestAccuracy = accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
if (location == null) {
|
||||
App.activity.showToastOnUiThread(App.activity
|
||||
.getString(R.string.error_last_location_unknown));
|
||||
return null;
|
||||
}
|
||||
|
||||
App.map.getMapPosition(mMapPosition);
|
||||
|
||||
if (mMapPosition.zoomLevel < SHOW_LOCATION_ZOOM)
|
||||
mMapPosition.setZoomLevel(SHOW_LOCATION_ZOOM);
|
||||
|
||||
mMapPosition.setPosition(location.getLatitude(), location.getLongitude());
|
||||
App.map.setMapPosition(mMapPosition);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
/***
|
||||
* LocationListener
|
||||
***/
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
|
||||
if (mMode == Mode.OFF)
|
||||
return;
|
||||
|
||||
double lat = location.getLatitude();
|
||||
double lon = location.getLongitude();
|
||||
|
||||
log.debug("update location " + lat + ":" + lon);
|
||||
|
||||
if (mSetCenter || mMode == Mode.SNAP) {
|
||||
mSetCenter = false;
|
||||
|
||||
App.map.getMapPosition(mMapPosition);
|
||||
mMapPosition.setPosition(lat, lon);
|
||||
App.map.setMapPosition(mMapPosition);
|
||||
}
|
||||
|
||||
mLocationOverlay.setPosition(lat, lon, location.getAccuracy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
public void setCenterOnFirstFix() {
|
||||
mSetCenter = true;
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (mMode != Mode.OFF) {
|
||||
log.debug("pause location listener");
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (mMode != Mode.OFF) {
|
||||
Criteria criteria = new Criteria();
|
||||
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||
String bestProvider = mLocationManager.getBestProvider(criteria, true);
|
||||
mLocationManager.requestLocationUpdates(bestProvider, 10000, 10, this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
331
vtm-app/src/org/oscim/app/location/LocationOverlay.java
Normal file
331
vtm-app/src/org/oscim/app/location/LocationOverlay.java
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright 2013 Ahmad Saleem
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.location;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import org.oscim.backend.GL;
|
||||
import org.oscim.core.Box;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.Point;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.map.Map;
|
||||
import org.oscim.renderer.GLShader;
|
||||
import org.oscim.renderer.GLState;
|
||||
import org.oscim.renderer.GLViewport;
|
||||
import org.oscim.renderer.LayerRenderer;
|
||||
import org.oscim.renderer.MapRenderer;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.utils.math.Interpolation;
|
||||
|
||||
import static org.oscim.backend.GLAdapter.gl;
|
||||
|
||||
public class LocationOverlay extends Layer {
|
||||
private final int SHOW_ACCURACY_ZOOM = 16;
|
||||
|
||||
private final Point mLocation = new Point();
|
||||
private double mRadius;
|
||||
|
||||
private final Compass mCompass;
|
||||
|
||||
public LocationOverlay(Map map, Compass compass) {
|
||||
super(map);
|
||||
mRenderer = new LocationIndicator(map);
|
||||
mCompass = compass;
|
||||
}
|
||||
|
||||
public void setPosition(double latitude, double longitude, double accuracy) {
|
||||
mLocation.x = MercatorProjection.longitudeToX(longitude);
|
||||
mLocation.y = MercatorProjection.latitudeToY(latitude);
|
||||
mRadius = accuracy / MercatorProjection.groundResolution(latitude, 1);
|
||||
((LocationIndicator) mRenderer).animate(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (enabled == isEnabled())
|
||||
return;
|
||||
|
||||
super.setEnabled(enabled);
|
||||
|
||||
if (!enabled)
|
||||
((LocationIndicator) mRenderer).animate(false);
|
||||
|
||||
mCompass.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public class LocationIndicator extends LayerRenderer {
|
||||
private int mShaderProgram;
|
||||
private int hVertexPosition;
|
||||
private int hMatrixPosition;
|
||||
private int hScale;
|
||||
private int hPhase;
|
||||
private int hDirection;
|
||||
|
||||
private final float CIRCLE_SIZE = 60;
|
||||
|
||||
private final static long ANIM_RATE = 50;
|
||||
private final static long INTERVAL = 2000;
|
||||
|
||||
private final Point mIndicatorPosition = new Point();
|
||||
|
||||
private final Point mScreenPoint = new Point();
|
||||
private final Box mBBox = new Box();
|
||||
|
||||
private boolean mInitialized;
|
||||
|
||||
private boolean mLocationIsVisible;
|
||||
|
||||
private boolean mRunAnim;
|
||||
private long mAnimStart;
|
||||
|
||||
public LocationIndicator(final Map map) {
|
||||
super();
|
||||
}
|
||||
|
||||
private void animate(boolean enable) {
|
||||
if (mRunAnim == enable)
|
||||
return;
|
||||
|
||||
mRunAnim = enable;
|
||||
if (!enable)
|
||||
return;
|
||||
|
||||
final Runnable action = new Runnable() {
|
||||
private long lastRun;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mRunAnim)
|
||||
return;
|
||||
|
||||
long diff = SystemClock.elapsedRealtime() - lastRun;
|
||||
mMap.postDelayed(this, Math.min(ANIM_RATE, diff));
|
||||
mMap.render();
|
||||
}
|
||||
};
|
||||
|
||||
mAnimStart = SystemClock.elapsedRealtime();
|
||||
mMap.postDelayed(action, ANIM_RATE);
|
||||
}
|
||||
|
||||
private float animPhase() {
|
||||
return (float) ((MapRenderer.frametime - mAnimStart) % INTERVAL) / INTERVAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(GLViewport v) {
|
||||
|
||||
if (!mInitialized) {
|
||||
init();
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
if (!isEnabled()) {
|
||||
setReady(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!v.changed() && isReady())
|
||||
return;
|
||||
|
||||
setReady(true);
|
||||
|
||||
int width = mMap.getWidth();
|
||||
int height = mMap.getHeight();
|
||||
|
||||
// clamp location to a position that can be
|
||||
// savely translated to screen coordinates
|
||||
v.getBBox(mBBox, 0);
|
||||
|
||||
double x = mLocation.x;
|
||||
double y = mLocation.y;
|
||||
|
||||
if (!mBBox.contains(mLocation)) {
|
||||
x = FastMath.clamp(x, mBBox.xmin, mBBox.xmax);
|
||||
y = FastMath.clamp(y, mBBox.ymin, mBBox.ymax);
|
||||
}
|
||||
|
||||
// get position of Location in pixel relative to
|
||||
// screen center
|
||||
v.toScreenPoint(x, y, mScreenPoint);
|
||||
|
||||
x = mScreenPoint.x + width / 2;
|
||||
y = mScreenPoint.y + height / 2;
|
||||
|
||||
// clip position to screen boundaries
|
||||
int visible = 0;
|
||||
|
||||
if (x > width - 5)
|
||||
x = width;
|
||||
else if (x < 5)
|
||||
x = 0;
|
||||
else
|
||||
visible++;
|
||||
|
||||
if (y > height - 5)
|
||||
y = height;
|
||||
else if (y < 5)
|
||||
y = 0;
|
||||
else
|
||||
visible++;
|
||||
|
||||
mLocationIsVisible = (visible == 2);
|
||||
|
||||
// set location indicator position
|
||||
v.fromScreenPoint(x, y, mIndicatorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GLViewport v) {
|
||||
|
||||
GLState.useProgram(mShaderProgram);
|
||||
GLState.blend(true);
|
||||
GLState.test(false, false);
|
||||
|
||||
GLState.enableVertexArrays(hVertexPosition, -1);
|
||||
MapRenderer.bindQuadVertexVBO(hVertexPosition/*, true*/);
|
||||
|
||||
float radius = CIRCLE_SIZE;
|
||||
|
||||
animate(true);
|
||||
boolean viewShed = false;
|
||||
if (!mLocationIsVisible /* || pos.zoomLevel < SHOW_ACCURACY_ZOOM */) {
|
||||
//animate(true);
|
||||
} else {
|
||||
if (v.pos.zoomLevel >= SHOW_ACCURACY_ZOOM)
|
||||
radius = (float) (mRadius * v.pos.scale);
|
||||
|
||||
viewShed = true;
|
||||
//animate(false);
|
||||
}
|
||||
gl.uniform1f(hScale, radius);
|
||||
|
||||
double x = mIndicatorPosition.x - v.pos.x;
|
||||
double y = mIndicatorPosition.y - v.pos.y;
|
||||
double tileScale = Tile.SIZE * v.pos.scale;
|
||||
|
||||
v.mvp.setTransScale((float) (x * tileScale), (float) (y * tileScale), 1);
|
||||
v.mvp.multiplyMM(v.viewproj, v.mvp);
|
||||
v.mvp.setAsUniform(hMatrixPosition);
|
||||
|
||||
if (!viewShed) {
|
||||
float phase = Math.abs(animPhase() - 0.5f) * 2;
|
||||
//phase = Interpolation.fade.apply(phase);
|
||||
phase = Interpolation.swing.apply(phase);
|
||||
|
||||
gl.uniform1f(hPhase, 0.8f + phase * 0.2f);
|
||||
} else {
|
||||
gl.uniform1f(hPhase, 1);
|
||||
}
|
||||
|
||||
if (viewShed && mLocationIsVisible) {
|
||||
float rotation = mCompass.getRotation() - 90;
|
||||
gl.uniform2f(hDirection,
|
||||
(float) Math.cos(Math.toRadians(rotation)),
|
||||
(float) Math.sin(Math.toRadians(rotation)));
|
||||
} else {
|
||||
gl.uniform2f(hDirection, 0, 0);
|
||||
}
|
||||
|
||||
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
private boolean init() {
|
||||
int shader = GLShader.createProgram(vShaderStr, fShaderStr);
|
||||
if (shader == 0)
|
||||
return false;
|
||||
|
||||
mShaderProgram = shader;
|
||||
hVertexPosition = gl.getAttribLocation(shader, "a_pos");
|
||||
hMatrixPosition = gl.getUniformLocation(shader, "u_mvp");
|
||||
hPhase = gl.getUniformLocation(shader, "u_phase");
|
||||
hScale = gl.getUniformLocation(shader, "u_scale");
|
||||
hDirection = gl.getUniformLocation(shader, "u_dir");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final static String vShaderStr = ""
|
||||
+ "precision mediump float;"
|
||||
+ "uniform mat4 u_mvp;"
|
||||
+ "uniform float u_phase;"
|
||||
+ "uniform float u_scale;"
|
||||
+ "attribute vec2 a_pos;"
|
||||
+ "varying vec2 v_tex;"
|
||||
+ "void main() {"
|
||||
+ " gl_Position = u_mvp * vec4(a_pos * u_scale * u_phase, 0.0, 1.0);"
|
||||
+ " v_tex = a_pos;"
|
||||
+ "}";
|
||||
|
||||
private final static String fShaderStr = ""
|
||||
+ "precision mediump float;"
|
||||
+ "varying vec2 v_tex;"
|
||||
+ "uniform float u_scale;"
|
||||
+ "uniform float u_phase;"
|
||||
+ "uniform vec2 u_dir;"
|
||||
|
||||
+ "void main() {"
|
||||
+ " float len = 1.0 - length(v_tex);"
|
||||
+ " if (u_dir.x == 0.0 && u_dir.y == 0.0){"
|
||||
+ " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * len;"
|
||||
+ " } else {"
|
||||
/// outer ring
|
||||
+ " float a = smoothstep(0.0, 2.0 / u_scale, len);"
|
||||
/// inner ring
|
||||
+ " float b = 0.5 * smoothstep(4.0 / u_scale, 5.0 / u_scale, len);"
|
||||
/// center point
|
||||
+ " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
|
||||
+ " vec2 dir = normalize(v_tex);"
|
||||
+ " float d = 1.0 - dot(dir, u_dir); "
|
||||
/// 0.5 width of viewshed
|
||||
+ " d = clamp(step(0.5, d), 0.4, 0.7);"
|
||||
/// - subtract inner from outer to create the outline
|
||||
/// - multiply by viewshed
|
||||
/// - add center point
|
||||
+ " a = d * (a - (b + c)) + c;"
|
||||
+ " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
|
||||
+ "}}";
|
||||
|
||||
//private final static String fShaderStr = ""
|
||||
// + "precision mediump float;"
|
||||
// + "varying vec2 v_tex;"
|
||||
// + "uniform float u_scale;"
|
||||
// + "uniform float u_phase;"
|
||||
// + "uniform vec2 u_dir;"
|
||||
// + "void main() {"
|
||||
// + " float len = 1.0 - length(v_tex);"
|
||||
// /// outer ring
|
||||
// + " float a = smoothstep(0.0, 2.0 / u_scale, len);"
|
||||
// /// inner ring
|
||||
// + " float b = 0.8 * smoothstep(3.0 / u_scale, 4.0 / u_scale, len);"
|
||||
// /// center point
|
||||
// + " float c = 0.5 * (1.0 - smoothstep(14.0 / u_scale, 16.0 / u_scale, 1.0 - len));"
|
||||
// + " vec2 dir = normalize(v_tex);"
|
||||
// + " float d = dot(dir, u_dir); "
|
||||
// /// 0.5 width of viewshed
|
||||
// + " d = clamp(smoothstep(0.7, 0.7 + 2.0/u_scale, d) * len, 0.0, 1.0);"
|
||||
// /// - subtract inner from outer to create the outline
|
||||
// /// - multiply by viewshed
|
||||
// /// - add center point
|
||||
// + " a = max(d, (a - (b + c)) + c);"
|
||||
// + " gl_FragColor = vec4(0.2, 0.2, 0.8, 1.0) * a;"
|
||||
// + "}";
|
||||
|
||||
}
|
||||
}
|
||||
219
vtm-app/src/org/oscim/app/preferences/CacheSizePreference.java
Normal file
219
vtm-app/src/org/oscim/app/preferences/CacheSizePreference.java
Normal file
@@ -0,0 +1,219 @@
|
||||
package org.oscim.app.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.oscim.app.R;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CacheSizePreference extends Preference implements OnSeekBarChangeListener {
|
||||
final static Logger log = LoggerFactory.getLogger(CacheSizePreference.class);
|
||||
|
||||
private static final String NS_OCIM_APP = "http://app.oscim.org";
|
||||
private static final int DEFAULT_VALUE = 50;
|
||||
|
||||
private int mMaxValue = 50;
|
||||
private int mMinValue = 0;
|
||||
private int mInterval = 1;
|
||||
private int mCurrentValue;
|
||||
private String mUnitsLeft = "";
|
||||
private String mUnitsRight = "";
|
||||
private SeekBar mSeekBar;
|
||||
|
||||
private TextView mStatusText;
|
||||
|
||||
public CacheSizePreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initPreference(context, attrs);
|
||||
}
|
||||
|
||||
public CacheSizePreference(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initPreference(context, attrs);
|
||||
}
|
||||
|
||||
private void initPreference(Context context, AttributeSet attrs) {
|
||||
setValuesFromXml(attrs);
|
||||
mSeekBar = new SeekBar(context, attrs);
|
||||
mSeekBar.setMax(mMaxValue - mMinValue);
|
||||
mSeekBar.setOnSeekBarChangeListener(this);
|
||||
}
|
||||
|
||||
private void setValuesFromXml(AttributeSet attrs) {
|
||||
//StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
|
||||
//double sdAvailSize = (double) stat.getAvailableBlocks()
|
||||
// * (double) stat.getBlockSize();
|
||||
//One binary megabyte equals 1,048,576 bytes.
|
||||
// otherwise we need an logarithmic scale to set a sane value with the slider...
|
||||
int megaAvailable = 50; //(int)sdAvailSize / 1048576;
|
||||
|
||||
mMaxValue = megaAvailable;//attrs.getAttributeIntValue(ANDROIDNS, "max", 100);
|
||||
mMinValue = attrs.getAttributeIntValue(NS_OCIM_APP, "min", 0);
|
||||
|
||||
mUnitsLeft = getAttributeStringValue(attrs, NS_OCIM_APP, "unitsLeft", "");
|
||||
//String units = getAttributeStringValue(attrs, NS_OCIM_APP, "units", "");
|
||||
mUnitsRight = "/" + String.valueOf(megaAvailable) + "MB";
|
||||
try {
|
||||
String newInterval = attrs.getAttributeValue(NS_OCIM_APP, "interval");
|
||||
if (newInterval != null)
|
||||
mInterval = Integer.parseInt(newInterval);
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getAttributeStringValue(AttributeSet attrs, String namespace, String name,
|
||||
String defaultValue) {
|
||||
String value = attrs.getAttributeValue(namespace, name);
|
||||
if (value == null)
|
||||
value = defaultValue;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateView(ViewGroup parent) {
|
||||
|
||||
RelativeLayout layout = null;
|
||||
|
||||
try {
|
||||
LayoutInflater mInflater = (LayoutInflater) getContext().getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
layout = (RelativeLayout) mInflater
|
||||
.inflate(R.layout.seek_bar_preference, parent, false);
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
|
||||
return layout;
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
try {
|
||||
// move our seekbar to the new view we've been given
|
||||
ViewParent oldContainer = mSeekBar.getParent();
|
||||
ViewGroup newContainer = (ViewGroup) view.findViewById(R.id.seekBarPrefBarContainer);
|
||||
|
||||
if (oldContainer != newContainer) {
|
||||
// remove the seekbar from the old view
|
||||
if (oldContainer != null) {
|
||||
((ViewGroup) oldContainer).removeView(mSeekBar);
|
||||
}
|
||||
// remove the existing seekbar (there may not be one) and add ours
|
||||
newContainer.removeAllViews();
|
||||
newContainer.addView(mSeekBar, ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("Error binding view: " + ex.toString());
|
||||
}
|
||||
|
||||
updateView(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a SeekBarPreference view with our current state
|
||||
*
|
||||
* @param view
|
||||
*/
|
||||
protected void updateView(View view) {
|
||||
|
||||
try {
|
||||
RelativeLayout layout = (RelativeLayout) view;
|
||||
|
||||
mStatusText = (TextView) layout.findViewById(R.id.seekBarPrefValue);
|
||||
mStatusText.setText(String.valueOf(mCurrentValue));
|
||||
mStatusText.setMinimumWidth(30);
|
||||
|
||||
mSeekBar.setProgress(mCurrentValue - mMinValue);
|
||||
|
||||
TextView unitsRight = (TextView) layout.findViewById(R.id.seekBarPrefUnitsRight);
|
||||
unitsRight.setText(mUnitsRight);
|
||||
|
||||
TextView unitsLeft = (TextView) layout.findViewById(R.id.seekBarPrefUnitsLeft);
|
||||
unitsLeft.setText(mUnitsLeft);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
int newValue = progress + mMinValue;
|
||||
|
||||
if (newValue > mMaxValue)
|
||||
newValue = mMaxValue;
|
||||
else if (newValue < mMinValue)
|
||||
newValue = mMinValue;
|
||||
else if (mInterval != 1 && newValue % mInterval != 0)
|
||||
newValue = Math.round(((float) newValue) / mInterval) * mInterval;
|
||||
|
||||
// change rejected, revert to the previous value
|
||||
if (!callChangeListener(newValue)) {
|
||||
seekBar.setProgress(mCurrentValue - mMinValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// change accepted, store it
|
||||
mCurrentValue = newValue;
|
||||
mStatusText.setText(String.valueOf(newValue));
|
||||
persistInt(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object onGetDefaultValue(TypedArray ta, int index) {
|
||||
|
||||
int defaultValue = ta.getInt(index, DEFAULT_VALUE);
|
||||
return defaultValue;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
|
||||
|
||||
if (restoreValue) {
|
||||
mCurrentValue = getPersistedInt(mCurrentValue);
|
||||
} else {
|
||||
int temp = 0;
|
||||
try {
|
||||
temp = (Integer) defaultValue;
|
||||
} catch (Exception ex) {
|
||||
log.error("Invalid default value: " + defaultValue.toString());
|
||||
}
|
||||
|
||||
persistInt(temp);
|
||||
mCurrentValue = temp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
68
vtm-app/src/org/oscim/app/preferences/EditPreferences.java
Normal file
68
vtm-app/src/org/oscim/app/preferences/EditPreferences.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.preferences;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.app.R;
|
||||
|
||||
/**
|
||||
* Activity to edit the application preferences.
|
||||
*/
|
||||
public class EditPreferences extends PreferenceActivity {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
Preference button = (Preference) findPreference("clear_cache");
|
||||
button.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference arg0) {
|
||||
App.activity.getMapLayers().deleteCache();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
overridePendingTransition(R.anim.slide_left, R.anim.slide_right2);
|
||||
}
|
||||
|
||||
// @TargetApi(11)
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||
// getActionBar().hide();
|
||||
|
||||
// check if the full screen mode should be activated
|
||||
// if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen",
|
||||
// false)) {
|
||||
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// } else {
|
||||
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// }
|
||||
}
|
||||
}
|
||||
153
vtm-app/src/org/oscim/app/preferences/SeekBarPreference.java
Normal file
153
vtm-app/src/org/oscim/app/preferences/SeekBarPreference.java
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.preference.DialogPreference;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* This abstract class provides all code for a seek bar preference. Deriving
|
||||
* classes only need to set the current and
|
||||
* maximum value of the seek bar. An optional text message above the seek bar is
|
||||
* also supported as well as an optional
|
||||
* current value message below the seek bar.
|
||||
*/
|
||||
abstract class SeekBarPreference extends DialogPreference implements OnSeekBarChangeListener {
|
||||
private TextView mCurrentValueTextView;
|
||||
private Editor mEditor;
|
||||
private SeekBar mPreferenceSeekBar;
|
||||
|
||||
/**
|
||||
* How much the value should increase when the seek bar is moved.
|
||||
*/
|
||||
int increment = 1;
|
||||
|
||||
/**
|
||||
* The maximum value of the seek bar.
|
||||
*/
|
||||
int max;
|
||||
|
||||
/**
|
||||
* Optional text message to display on top of the seek bar.
|
||||
*/
|
||||
String messageText;
|
||||
|
||||
/**
|
||||
* The SharedPreferences instance that is used.
|
||||
*/
|
||||
final SharedPreferences preferencesDefault;
|
||||
|
||||
/**
|
||||
* The current value of the seek bar.
|
||||
*/
|
||||
int seekBarCurrentValue;
|
||||
|
||||
/**
|
||||
* Create a new seek bar preference.
|
||||
*
|
||||
* @param context the context of the seek bar preferences activity.
|
||||
* @param attrs A set of attributes (currently ignored).
|
||||
*/
|
||||
SeekBarPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
preferencesDefault = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// check if the "OK" button was pressed and the seek bar value has changed
|
||||
if (which == DialogInterface.BUTTON_POSITIVE
|
||||
&& seekBarCurrentValue != mPreferenceSeekBar.getProgress()) {
|
||||
// get the value of the seek bar and save it in the preferences
|
||||
seekBarCurrentValue = mPreferenceSeekBar.getProgress();
|
||||
mEditor = preferencesDefault.edit();
|
||||
mEditor.putInt(getKey(), seekBarCurrentValue);
|
||||
mEditor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (mCurrentValueTextView != null) {
|
||||
mCurrentValueTextView.setText(getCurrentValueText(progress));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateDialogView() {
|
||||
// create a layout for the optional text messageText and the seek bar
|
||||
LinearLayout linearLayout = new LinearLayout(getContext());
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
linearLayout.setPadding(20, 10, 20, 10);
|
||||
|
||||
// check if a text message should appear above the seek bar
|
||||
if (messageText != null) {
|
||||
// create a text view for the text messageText
|
||||
TextView messageTextView = new TextView(getContext());
|
||||
messageTextView.setText(messageText);
|
||||
messageTextView.setPadding(0, 0, 0, 20);
|
||||
// add the text message view to the layout
|
||||
linearLayout.addView(messageTextView);
|
||||
}
|
||||
|
||||
// create the seek bar and set the maximum and current value
|
||||
mPreferenceSeekBar = new SeekBar(getContext());
|
||||
mPreferenceSeekBar.setOnSeekBarChangeListener(this);
|
||||
mPreferenceSeekBar.setMax(max);
|
||||
mPreferenceSeekBar.setProgress(Math.min(seekBarCurrentValue, max));
|
||||
mPreferenceSeekBar.setKeyProgressIncrement(increment);
|
||||
mPreferenceSeekBar.setPadding(0, 0, 0, 10);
|
||||
// add the seek bar to the layout
|
||||
linearLayout.addView(mPreferenceSeekBar);
|
||||
|
||||
// create the text view for the current value below the seek bar
|
||||
mCurrentValueTextView = new TextView(getContext());
|
||||
mCurrentValueTextView.setText(getCurrentValueText(mPreferenceSeekBar.getProgress()));
|
||||
mCurrentValueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
// add the current value text view to the layout
|
||||
linearLayout.addView(mCurrentValueTextView);
|
||||
|
||||
return linearLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value text.
|
||||
*
|
||||
* @param progress the current progress level of the seek bar.
|
||||
* @return the new current value text
|
||||
*/
|
||||
abstract String getCurrentValueText(int progress);
|
||||
}
|
||||
182
vtm-app/src/org/oscim/overlay/DistanceTouchOverlay.java
Normal file
182
vtm-app/src/org/oscim/overlay/DistanceTouchOverlay.java
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2013 Ahmad Saleem
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.overlay;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.event.Gesture;
|
||||
import org.oscim.event.GestureListener;
|
||||
import org.oscim.event.MotionEvent;
|
||||
import org.oscim.layers.Layer;
|
||||
import org.oscim.map.Map;
|
||||
import org.osmdroid.overlays.MapEventsReceiver;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class DistanceTouchOverlay extends Layer implements Map.InputListener,
|
||||
GestureListener {
|
||||
|
||||
private static final int LONGPRESS_THRESHOLD = 800;
|
||||
|
||||
private Timer mLongpressTimer;
|
||||
|
||||
private float mPrevX1, mPrevX2, mPrevY1, mPrevY2;
|
||||
private float mCurX1, mCurX2, mCurY1, mCurY2;
|
||||
|
||||
// private final static int POINTER_UP = -1;
|
||||
// private int mPointer1 = POINTER_UP;
|
||||
// private int mPointer2 = POINTER_UP;
|
||||
|
||||
private final MapEventsReceiver mReceiver;
|
||||
|
||||
/**
|
||||
* @param map the Map
|
||||
* @param receiver the object that will receive/handle the events. It must
|
||||
* implement MapEventsReceiver interface.
|
||||
*/
|
||||
public DistanceTouchOverlay(Map map, MapEventsReceiver receiver) {
|
||||
super(map);
|
||||
mReceiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
|
||||
if (mLongpressTimer != null) {
|
||||
mLongpressTimer.cancel();
|
||||
mLongpressTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEvent(Event event, MotionEvent e) {
|
||||
|
||||
int action = e.getAction() & MotionEvent.ACTION_MASK;
|
||||
|
||||
if ((action == MotionEvent.ACTION_CANCEL)) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLongpressTimer != null) {
|
||||
// any pointer up while long press detection
|
||||
// cancels timer
|
||||
if (action == MotionEvent.ACTION_POINTER_UP
|
||||
|| action == MotionEvent.ACTION_UP) {
|
||||
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// two fingers must still be down, tested
|
||||
// one above.
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
// update pointer positions
|
||||
// int idx1 = e.findPointerIndex(mPointer1);
|
||||
// int idx2 = e.findPointerIndex(mPointer2);
|
||||
|
||||
mCurX1 = e.getX(0);
|
||||
mCurY1 = e.getY(0);
|
||||
mCurX2 = e.getX(1);
|
||||
mCurY2 = e.getY(1);
|
||||
|
||||
// cancel if moved one finger more than 50 pixel
|
||||
float maxSq = 10 * 10;
|
||||
float d = (mCurX1 - mPrevX1) * (mCurX1 - mPrevX1)
|
||||
+ (mCurY1 - mPrevY1) * (mCurY1 - mPrevY1);
|
||||
if (d > maxSq) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
d = (mCurX2 - mPrevX2) * (mCurX2 - mPrevX2)
|
||||
+ (mCurY2 - mPrevY2) * (mCurY2 - mPrevY2);
|
||||
if (d > maxSq) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((action == MotionEvent.ACTION_POINTER_DOWN)
|
||||
&& (e.getPointerCount() == 2)) {
|
||||
// App.log.debug("down");
|
||||
|
||||
// keep track of pointer ids, only
|
||||
// use these for gesture, ignoring
|
||||
// more than two pointer
|
||||
|
||||
// mPointer1 = e.getPointerId(0);
|
||||
// mPointer2 = e.getPointerId(1);
|
||||
|
||||
if (mLongpressTimer == null) {
|
||||
// start timer, keep initial down position
|
||||
mCurX1 = mPrevX1 = e.getX(0);
|
||||
mCurY1 = mPrevY1 = e.getY(0);
|
||||
mCurX2 = mPrevX2 = e.getX(1);
|
||||
mCurY2 = mPrevY2 = e.getY(1);
|
||||
runLongpressTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean onLongPress(MotionEvent e) {
|
||||
// // dont forward long press when two fingers are down.
|
||||
// // maybe should be only done if our timer is still running.
|
||||
// // ... not sure if this is even needed
|
||||
// GeoPoint p = mMap.getViewport().fromScreenPoint(e.getX(), e.getY());
|
||||
// return mReceiver.longPressHelper(p);
|
||||
// }
|
||||
|
||||
public void runLongpressTimer() {
|
||||
// mMap.postDelayed(action, delay);
|
||||
|
||||
mLongpressTimer = new Timer();
|
||||
mLongpressTimer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final GeoPoint p1 = mMap.viewport().fromScreenPoint(mCurX1,
|
||||
mCurY1);
|
||||
final GeoPoint p2 = mMap.viewport().fromScreenPoint(mCurX2,
|
||||
mCurY2);
|
||||
|
||||
mMap.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mReceiver.longPressHelper(p1, p2);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, LONGPRESS_THRESHOLD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGesture(Gesture g, MotionEvent e) {
|
||||
if (g instanceof Gesture.LongPress) {
|
||||
GeoPoint p = mMap.viewport().fromScreenPoint(e.getX(), e.getY());
|
||||
return mReceiver.longPressHelper(p);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
163
vtm-app/src/org/osmdroid/location/FlickrPOIProvider.java
Normal file
163
vtm-app/src/org/osmdroid/location/FlickrPOIProvider.java
Normal file
@@ -0,0 +1,163 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* POI Provider using Flickr service to get geolocalized photos.
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see "http://www.flickr.com/services/api/flickr.photos.search.html"
|
||||
*/
|
||||
public class FlickrPOIProvider implements POIProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(FlickrPOIProvider.class);
|
||||
|
||||
protected String mApiKey;
|
||||
private final static String PHOTO_URL = "http://www.flickr.com/photos/%s/%s/sizes/o/in/photostream/";
|
||||
|
||||
/**
|
||||
* @param apiKey the registered API key to give to Flickr service.
|
||||
* @see "http://www.flickr.com/help/api/"
|
||||
*/
|
||||
public FlickrPOIProvider(String apiKey) {
|
||||
mApiKey = apiKey;
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox boundingBox, int maxResults) {
|
||||
StringBuffer url = new StringBuffer(
|
||||
"http://api.flickr.com/services/rest/?method=flickr.photos.search");
|
||||
url.append("&api_key=" + mApiKey);
|
||||
url.append("&bbox=" + boundingBox.getMinLongitude());
|
||||
url.append("," + boundingBox.getMinLatitude());
|
||||
url.append("," + boundingBox.getMaxLongitude());
|
||||
url.append("," + boundingBox.getMaxLatitude());
|
||||
url.append("&has_geo=1");
|
||||
// url.append("&geo_context=2");
|
||||
// url.append("&is_commons=true");
|
||||
url.append("&format=json&nojsoncallback=1");
|
||||
url.append("&per_page=" + maxResults);
|
||||
// From Flickr doc:
|
||||
// "Geo queries require some sort of limiting agent in order to prevent the database from crying."
|
||||
// And min_date_upload is considered as a limiting agent. So:
|
||||
url.append("&min_upload_date=2005/01/01");
|
||||
|
||||
// Ask to provide some additional attributes we will need:
|
||||
url.append("&extras=geo,url_sq");
|
||||
url.append("&sort=interestingness-desc");
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/* public POI getPhoto(String photoId){ String url =
|
||||
* "http://api.flickr.com/services/rest/?method=flickr.photos.getInfo"
|
||||
* +
|
||||
* "&api_key=" + mApiKey + "&photo_id=" + photo Id +
|
||||
* "&format=json&nojsoncallback=1"; log.debug( * "getPhoto:"+url); String
|
||||
* jString =
|
||||
* BonusPackHelper.requestStringFromUrl(url); if (jString == null)
|
||||
* {
|
||||
* log.error( * "FlickrPOIProvider: request failed.");
|
||||
* return null; } try { POI poi = new POI(POI.POI_SERVICE_FLICKR);
|
||||
* JSONObject jRoot = new JSONObject(jString); JSONObject jPhoto =
|
||||
* jRoot.getJSONObject("photo"); JSONObject jLocation =
|
||||
* jPhoto.getJSONObject("location"); poi.mLocation = new GeoPoint(
|
||||
* jLocation.getDouble("latitude"),
|
||||
* jLocation.getDouble("longitude"));
|
||||
* poi.mId = Long.parseLong(photoId); JSONObject jTitle =
|
||||
* jPhoto.getJSONObject("title"); poi.mType =
|
||||
* jTitle.getString("_content");
|
||||
* JSONObject jDescription = jPhoto.getJSONObject("description");
|
||||
* poi.mDescription = jDescription.getString("_content");
|
||||
* //truncate
|
||||
* description if too long: if (poi.mDescription.length() > 300){
|
||||
* poi.mDescription = poi.mDescription.substring(0, 300) +
|
||||
* " (...)"; }
|
||||
* String farm = jPhoto.getString("farm"); String server =
|
||||
* jPhoto.getString("server"); String secret =
|
||||
* jPhoto.getString("secret");
|
||||
* JSONObject jOwner = jPhoto.getJSONObject("owner"); String nsid
|
||||
* =
|
||||
* jOwner.getString("nsid"); poi.mThumbnailPath =
|
||||
* "http://farm"+farm+".staticflickr.com/"
|
||||
* +server+"/"+photoId+"_"+secret+"_s.jpg"; poi.mUrl =
|
||||
* "http://www.flickr.com/photos/"+nsid+"/"+photoId; return poi;
|
||||
* }catch
|
||||
* (JSONException e) { e.printStackTrace(); return null; } } */
|
||||
|
||||
/**
|
||||
* @param fullUrl ...
|
||||
* @return the list of POI
|
||||
*/
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
// for local debug: fullUrl = "http://10.0.2.2/flickr_mockup.json";
|
||||
log.debug("FlickrPOIProvider:get:" + fullUrl);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
|
||||
if (jString == null) {
|
||||
log.error("FlickrPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jRoot = new JSONObject(jString);
|
||||
JSONObject jPhotos = jRoot.getJSONObject("photos");
|
||||
JSONArray jPhotoArray = jPhotos.getJSONArray("photo");
|
||||
int n = jPhotoArray.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jPhoto = jPhotoArray.getJSONObject(i);
|
||||
|
||||
String photoId = jPhoto.getString("id");
|
||||
if (mPrevious != null && mPrevious.containsKey(photoId))
|
||||
continue;
|
||||
|
||||
POI poi = new POI(POI.POI_SERVICE_FLICKR);
|
||||
poi.location = new GeoPoint(
|
||||
jPhoto.getDouble("latitude"),
|
||||
jPhoto.getDouble("longitude"));
|
||||
poi.id = photoId; //Long.parseLong(photoId);
|
||||
poi.type = jPhoto.getString("title");
|
||||
poi.thumbnailPath = jPhoto.getString("url_sq");
|
||||
String owner = jPhoto.getString("owner");
|
||||
// the default flickr link viewer doesnt work with mobile browsers...
|
||||
// poi.url = "http://www.flickr.com/photos/" + owner + "/" + photoId + "/sizes/o/in/photostream/";
|
||||
|
||||
poi.url = String.format(PHOTO_URL, owner, photoId);
|
||||
|
||||
pois.add(poi);
|
||||
}
|
||||
// int total = jPhotos.getInt("total");
|
||||
// log.debug(on a total of:" + total);
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox ...
|
||||
* @param maxResults ...
|
||||
* @return list of POI, Flickr photos inside the bounding box.
|
||||
* Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, String query, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
HashMap<String, POI> mPrevious;
|
||||
|
||||
public void setPrevious(HashMap<String, POI> previous) {
|
||||
mPrevious = previous;
|
||||
}
|
||||
|
||||
}
|
||||
186
vtm-app/src/org/osmdroid/location/FourSquareProvider.java
Normal file
186
vtm-app/src/org/osmdroid/location/FourSquareProvider.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.osmdroid.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class FourSquareProvider implements POIProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(FourSquareProvider.class);
|
||||
|
||||
// https://developer.foursquare.com/docs/venues/search
|
||||
// https://developer.foursquare.com/docs/responses/venue
|
||||
// https://apigee.com/console/foursquare
|
||||
|
||||
protected String mApiKey;
|
||||
|
||||
// private static HashMap<String, Bitmap> mIcons =
|
||||
// (HashMap<String,Bitmap>)Collections.synchronizedMap(new HashMap<String, Bitmap>());
|
||||
|
||||
/**
|
||||
* @param clientSecret the registered API key to give to Flickr service.
|
||||
* @see "http://www.flickr.com/help/api/"
|
||||
*/
|
||||
public FourSquareProvider(String clientId, String clientSecret) {
|
||||
mApiKey = "client_id=" + clientId + "&client_secret=" + clientSecret;
|
||||
}
|
||||
|
||||
//"https://api.foursquare.com/v2/venues/search?v=20120321&intent=checkin&ll=53.06,8.8&client_id=ZUN4ZMNZUFT3Z5QQZNMQ3ACPL4OJMBFGO15TYX51D5MHCIL3&client_secret=X1RXCVF4VVSG1Y2FUDQJLKQUC1WF4XXKIMK2STXKACLPDGLY
|
||||
@SuppressWarnings("deprecation")
|
||||
private String getUrlInside(BoundingBox boundingBox, String query, int maxResults) {
|
||||
StringBuffer url = new StringBuffer(
|
||||
"https://api.foursquare.com/v2/venues/search?v=20120321"
|
||||
+ "&intent=browse"
|
||||
+ "&client_id=ZUN4ZMNZUFT3Z5QQZNMQ3ACPL4OJMBFGO15TYX51D5MHCIL3"
|
||||
+ "&client_secret=X1RXCVF4VVSG1Y2FUDQJLKQUC1WF4XXKIMK2STXKACLPDGLY");
|
||||
url.append("&sw=");
|
||||
url.append(boundingBox.getMinLatitude());
|
||||
url.append(',');
|
||||
url.append(boundingBox.getMinLongitude());
|
||||
url.append("&ne=");
|
||||
url.append(boundingBox.getMaxLatitude());
|
||||
url.append(',');
|
||||
url.append(boundingBox.getMaxLongitude());
|
||||
url.append("&limit=");
|
||||
url.append(maxResults);
|
||||
if (query != null)
|
||||
url.append("&query=" + URLEncoder.encode(query));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fullUrl ...
|
||||
* @return the list of POI
|
||||
*/
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
// for local debug: fullUrl = "http://10.0.2.2/flickr_mockup.json";
|
||||
log.debug("FlickrPOIProvider:get:" + fullUrl);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
|
||||
if (jString == null) {
|
||||
log.error("FlickrPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jRoot = new JSONObject(jString);
|
||||
|
||||
JSONObject jResponse = jRoot.getJSONObject("response");
|
||||
JSONArray jVenueArray = jResponse.getJSONArray("venues");
|
||||
int n = jVenueArray.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jVenue = jVenueArray.getJSONObject(i);
|
||||
|
||||
POI poi = new POI(POI.POI_SERVICE_4SQUARE);
|
||||
poi.id = jVenue.getString("id");
|
||||
poi.type = jVenue.getString("name");
|
||||
// poi.url = jVenue.optString("url", null);
|
||||
poi.url = "https://foursquare.com/v/" + poi.id;
|
||||
|
||||
JSONObject jLocation = jVenue.getJSONObject("location");
|
||||
poi.location = new GeoPoint(
|
||||
jLocation.getDouble("lat"),
|
||||
jLocation.getDouble("lng"));
|
||||
poi.description = jLocation.optString("address", null);
|
||||
|
||||
JSONArray jCategories = jVenue.getJSONArray("categories");
|
||||
if (jCategories.length() > 0) {
|
||||
JSONObject jCategory = jCategories.getJSONObject(0);
|
||||
String icon = jCategory.getJSONObject("icon").getString("prefix");
|
||||
poi.thumbnailPath = icon + 44 + ".png";
|
||||
poi.category = jCategory.optString("name");
|
||||
}
|
||||
pois.add(poi);
|
||||
}
|
||||
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox ...
|
||||
* @param maxResults ...
|
||||
* @return list of POI, Flickr photos inside the bounding box.
|
||||
* Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, String query, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, query, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
public static void browse(final Context context, POI poi) {
|
||||
// get the right url from redirect, could also parse the result from querying venueid...
|
||||
new AsyncTask<POI, Void, String>() {
|
||||
|
||||
@Override
|
||||
protected String doInBackground(POI... params) {
|
||||
POI poi = params[0];
|
||||
if (poi == null)
|
||||
return null;
|
||||
try {
|
||||
URL url = new URL(poi.url);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setInstanceFollowRedirects(false);
|
||||
|
||||
String redirect = conn.getHeaderField("Location");
|
||||
if (redirect != null) {
|
||||
log.debug(redirect);
|
||||
return redirect;
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://foursquare.com"
|
||||
+ result));
|
||||
context.startActivity(myIntent);
|
||||
|
||||
}
|
||||
}.execute(poi);
|
||||
|
||||
}
|
||||
}
|
||||
215
vtm-app/src/org/osmdroid/location/GeoNamesPOIProvider.java
Normal file
215
vtm-app/src/org/osmdroid/location/GeoNamesPOIProvider.java
Normal file
@@ -0,0 +1,215 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* POI Provider using GeoNames services. Currently, "find Nearby Wikipedia" and
|
||||
* "Wikipedia Articles in Bounding Box" services.
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see "http://www.geonames.org"
|
||||
*/
|
||||
public class GeoNamesPOIProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(GeoNamesPOIProvider.class);
|
||||
|
||||
protected String mUserName;
|
||||
|
||||
/**
|
||||
* @param account the registered "username" to give to GeoNames service.
|
||||
* @see "http://www.geonames.org/login"
|
||||
*/
|
||||
public GeoNamesPOIProvider(String account) {
|
||||
mUserName = account;
|
||||
}
|
||||
|
||||
private String getUrlCloseTo(GeoPoint p, int maxResults, double maxDistance) {
|
||||
StringBuffer url = new StringBuffer("http://api.geonames.org/findNearbyWikipediaJSON?");
|
||||
url.append("lat=" + p.getLatitude());
|
||||
url.append("&lng=" + p.getLongitude());
|
||||
url.append("&maxRows=" + maxResults);
|
||||
url.append("&radius=" + maxDistance); //km
|
||||
url.append("&lang=" + Locale.getDefault().getLanguage());
|
||||
url.append("&username=" + mUserName);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox boundingBox, int maxResults) {
|
||||
StringBuffer url = new StringBuffer("http://api.geonames.org/wikipediaBoundingBoxJSON?");
|
||||
url.append("south=" + boundingBox.getMinLatitude());
|
||||
url.append("&north=" + boundingBox.getMaxLatitude());
|
||||
url.append("&west=" + boundingBox.getMinLongitude());
|
||||
url.append("&east=" + boundingBox.getMaxLongitude());
|
||||
url.append("&maxRows=" + maxResults);
|
||||
url.append("&lang=" + Locale.getDefault().getLanguage());
|
||||
url.append("&username=" + mUserName);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fullUrl ...
|
||||
* @return the list of POI
|
||||
*/
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
log.debug("GeoNamesPOIProvider:get:" + fullUrl);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
|
||||
if (jString == null) {
|
||||
log.error("GeoNamesPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jRoot = new JSONObject(jString);
|
||||
JSONArray jPlaceIds = jRoot.getJSONArray("geonames");
|
||||
int n = jPlaceIds.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jPlace = jPlaceIds.getJSONObject(i);
|
||||
POI poi = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
|
||||
poi.location = new GeoPoint(jPlace.getDouble("lat"),
|
||||
jPlace.getDouble("lng"));
|
||||
poi.category = jPlace.optString("feature");
|
||||
poi.type = jPlace.getString("title");
|
||||
poi.description = jPlace.optString("summary");
|
||||
poi.thumbnailPath = jPlace.optString("thumbnailImg", null);
|
||||
/* This makes loading too long. Thumbnail loading will be done
|
||||
* only when needed, with POI.getThumbnail() if
|
||||
* (poi.mThumbnailPath != null){ poi.mThumbnail =
|
||||
* BonusPackHelper.loadBitmap(poi.mThumbnailPath); } */
|
||||
poi.url = jPlace.optString("wikipediaUrl", null);
|
||||
if (poi.url != null)
|
||||
poi.url = "http://" + poi.url;
|
||||
poi.rank = jPlace.optInt("rank", 0);
|
||||
//other attributes: distance?
|
||||
pois.add(poi);
|
||||
}
|
||||
log.debug("done");
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//XML parsing seems 2 times slower than JSON parsing
|
||||
public ArrayList<POI> getThemXML(String fullUrl) {
|
||||
log.debug("GeoNamesPOIProvider:get:" + fullUrl);
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(fullUrl);
|
||||
InputStream stream = connection.getStream();
|
||||
if (stream == null) {
|
||||
return null;
|
||||
}
|
||||
GeoNamesXMLHandler handler = new GeoNamesXMLHandler();
|
||||
try {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(stream, handler);
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
connection.close();
|
||||
log.debug("done");
|
||||
return handler.mPOIs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position ...
|
||||
* @param maxResults ...
|
||||
* @param maxDistance ... in km. 20 km max for the free service.
|
||||
* @return list of POI, Wikipedia entries close to the position. Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOICloseTo(GeoPoint position,
|
||||
int maxResults, double maxDistance) {
|
||||
String url = getUrlCloseTo(position, maxResults, maxDistance);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox ...
|
||||
* @param maxResults ...
|
||||
* @return list of POI, Wikipedia entries inside the bounding box. Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
}
|
||||
|
||||
class GeoNamesXMLHandler extends DefaultHandler {
|
||||
|
||||
private String mString;
|
||||
double mLat, mLng;
|
||||
POI mPOI;
|
||||
ArrayList<POI> mPOIs;
|
||||
|
||||
public GeoNamesXMLHandler() {
|
||||
mPOIs = new ArrayList<POI>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String name,
|
||||
Attributes attributes) {
|
||||
if (localName.equals("entry")) {
|
||||
mPOI = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
|
||||
}
|
||||
mString = new String();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
String chars = new String(ch, start, length);
|
||||
mString = mString.concat(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String name) {
|
||||
if (localName.equals("lat")) {
|
||||
mLat = Double.parseDouble(mString);
|
||||
} else if (localName.equals("lng")) {
|
||||
mLng = Double.parseDouble(mString);
|
||||
} else if (localName.equals("feature")) {
|
||||
mPOI.category = mString;
|
||||
} else if (localName.equals("title")) {
|
||||
mPOI.type = mString;
|
||||
} else if (localName.equals("summary")) {
|
||||
mPOI.description = mString;
|
||||
} else if (localName.equals("thumbnailImg")) {
|
||||
if (mString != null && !mString.equals(""))
|
||||
mPOI.thumbnailPath = mString;
|
||||
} else if (localName.equals("wikipediaUrl")) {
|
||||
if (mString != null && !mString.equals(""))
|
||||
mPOI.url = "http://" + mString;
|
||||
} else if (localName.equals("rank")) {
|
||||
mPOI.rank = Integer.parseInt(mString);
|
||||
} else if (localName.equals("entry")) {
|
||||
mPOI.location = new GeoPoint(mLat, mLng);
|
||||
mPOIs.add(mPOI);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
208
vtm-app/src/org/osmdroid/location/GeocoderNominatim.java
Normal file
208
vtm-app/src/org/osmdroid/location/GeocoderNominatim.java
Normal file
@@ -0,0 +1,208 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Address;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Implements an equivalent to Android Geocoder class, based on OpenStreetMap
|
||||
* data and Nominatim API. <br>
|
||||
* See http://wiki.openstreetmap.org/wiki/Nominatim or
|
||||
* http://open.mapquestapi.com/nominatim/
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class GeocoderNominatim {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(GeocoderNominatim.class);
|
||||
|
||||
public static final String NOMINATIM_SERVICE_URL = "http://nominatim.openstreetmap.org/";
|
||||
public static final String MAPQUEST_SERVICE_URL = "http://open.mapquestapi.com/nominatim/v1/";
|
||||
|
||||
protected Locale mLocale;
|
||||
protected String mServiceUrl;
|
||||
|
||||
/**
|
||||
* @param context ...
|
||||
* @param locale ...
|
||||
*/
|
||||
protected void init(Context context, Locale locale) {
|
||||
mLocale = locale;
|
||||
setService(NOMINATIM_SERVICE_URL); //default service
|
||||
}
|
||||
|
||||
public GeocoderNominatim(Context context, Locale locale) {
|
||||
init(context, locale);
|
||||
}
|
||||
|
||||
public GeocoderNominatim(Context context) {
|
||||
init(context, Locale.getDefault());
|
||||
}
|
||||
|
||||
static public boolean isPresent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the url of the Nominatim service provider to use. Can be one of
|
||||
* the predefined (NOMINATIM_SERVICE_URL or MAPQUEST_SERVICE_URL), or
|
||||
* another one, your local instance of Nominatim for instance.
|
||||
*
|
||||
* @param serviceUrl ...
|
||||
*/
|
||||
public void setService(String serviceUrl) {
|
||||
mServiceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an Android Address object from the Nominatim address in JSON
|
||||
* format. Current implementation is mainly targeting french addresses, and
|
||||
* will be quite basic on other countries.
|
||||
*
|
||||
* @param jResult ...
|
||||
* @return ...
|
||||
* @throws JSONException ...
|
||||
*/
|
||||
protected Address buildAndroidAddress(JSONObject jResult) throws JSONException {
|
||||
Address gAddress = new Address(mLocale);
|
||||
gAddress.setLatitude(jResult.getDouble("lat"));
|
||||
gAddress.setLongitude(jResult.getDouble("lon"));
|
||||
|
||||
JSONObject jAddress = jResult.getJSONObject("address");
|
||||
|
||||
int addressIndex = 0;
|
||||
if (jAddress.has("road")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("road"));
|
||||
gAddress.setThoroughfare(jAddress.getString("road"));
|
||||
}
|
||||
if (jAddress.has("suburb")) {
|
||||
//gAddress.setAddressLine(addressIndex++, jAddress.getString("suburb"));
|
||||
//not kept => often introduce "noise" in the address.
|
||||
gAddress.setSubLocality(jAddress.getString("suburb"));
|
||||
}
|
||||
if (jAddress.has("postcode")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("postcode"));
|
||||
gAddress.setPostalCode(jAddress.getString("postcode"));
|
||||
}
|
||||
|
||||
if (jAddress.has("city")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("city"));
|
||||
gAddress.setLocality(jAddress.getString("city"));
|
||||
} else if (jAddress.has("town")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("town"));
|
||||
gAddress.setLocality(jAddress.getString("town"));
|
||||
} else if (jAddress.has("village")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("village"));
|
||||
gAddress.setLocality(jAddress.getString("village"));
|
||||
}
|
||||
|
||||
if (jAddress.has("county")) { //France: departement
|
||||
gAddress.setSubAdminArea(jAddress.getString("county"));
|
||||
}
|
||||
if (jAddress.has("state")) { //France: region
|
||||
gAddress.setAdminArea(jAddress.getString("state"));
|
||||
}
|
||||
if (jAddress.has("country")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("country"));
|
||||
gAddress.setCountryName(jAddress.getString("country"));
|
||||
}
|
||||
if (jAddress.has("country_code"))
|
||||
gAddress.setCountryCode(jAddress.getString("country_code"));
|
||||
|
||||
/* Other possible OSM tags in Nominatim results not handled yet: subway,
|
||||
* golf_course, bus_stop, parking,... house, house_number, building
|
||||
* city_district (13e Arrondissement) road => or highway, ... sub-city
|
||||
* (like suburb) => locality, isolated_dwelling, hamlet ...
|
||||
* state_district */
|
||||
|
||||
return gAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param latitude ...
|
||||
* @param longitude ...
|
||||
* @param maxResults ...
|
||||
* @return ...
|
||||
* @throws IOException ...
|
||||
*/
|
||||
public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
|
||||
throws IOException {
|
||||
String url = mServiceUrl
|
||||
+ "reverse?"
|
||||
+ "format=json"
|
||||
+ "&accept-language=" + mLocale.getLanguage()
|
||||
//+ "&addressdetails=1"
|
||||
+ "&lat=" + latitude
|
||||
+ "&lon=" + longitude;
|
||||
log.debug("GeocoderNominatim::getFromLocation:" + url);
|
||||
String result = BonusPackHelper.requestStringFromUrl(url);
|
||||
//log.debug(result);
|
||||
if (result == null)
|
||||
throw new IOException();
|
||||
try {
|
||||
JSONObject jResult = new JSONObject(result);
|
||||
Address gAddress = buildAndroidAddress(jResult);
|
||||
List<Address> list = new ArrayList<Address>();
|
||||
list.add(gAddress);
|
||||
return list;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Address> getFromLocationName(String locationName, int maxResults,
|
||||
double lowerLeftLatitude, double lowerLeftLongitude,
|
||||
double upperRightLatitude, double upperRightLongitude)
|
||||
throws IOException {
|
||||
String url = mServiceUrl
|
||||
+ "search?"
|
||||
+ "format=json"
|
||||
+ "&accept-language=" + mLocale.getLanguage()
|
||||
+ "&addressdetails=1"
|
||||
+ "&limit=" + maxResults
|
||||
+ "&q=" + URLEncoder.encode(locationName, "UTF-8");
|
||||
if (lowerLeftLatitude != 0.0 && lowerLeftLongitude != 0.0) {
|
||||
//viewbox = left, top, right, bottom:
|
||||
url += "&viewbox=" + lowerLeftLongitude
|
||||
+ "," + upperRightLatitude
|
||||
+ "," + upperRightLongitude
|
||||
+ "," + lowerLeftLatitude
|
||||
+ "&bounded=1";
|
||||
}
|
||||
log.debug("GeocoderNominatim::getFromLocationName:" + url);
|
||||
String result = BonusPackHelper.requestStringFromUrl(url);
|
||||
//log.debug(result);
|
||||
if (result == null)
|
||||
throw new IOException();
|
||||
try {
|
||||
JSONArray jResults = new JSONArray(result);
|
||||
List<Address> list = new ArrayList<Address>();
|
||||
for (int i = 0; i < jResults.length(); i++) {
|
||||
JSONObject jResult = jResults.getJSONObject(i);
|
||||
Address gAddress = buildAndroidAddress(jResult);
|
||||
list.add(gAddress);
|
||||
}
|
||||
return list;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Address> getFromLocationName(String locationName, int maxResults)
|
||||
throws IOException {
|
||||
return getFromLocationName(locationName, maxResults, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
}
|
||||
192
vtm-app/src/org/osmdroid/location/NominatimPOIProvider.java
Normal file
192
vtm-app/src/org/osmdroid/location/NominatimPOIProvider.java
Normal file
@@ -0,0 +1,192 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* POI Provider using Nominatim service. <br>
|
||||
* See https://wiki.openstreetmap.org/wiki/Nominatim<br>
|
||||
* and http://open.mapquestapi.com/nominatim/<br>
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class NominatimPOIProvider implements POIProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(NominatimPOIProvider.class);
|
||||
|
||||
/* As the doc lacks a lot of features, source code may help:
|
||||
* https://trac.openstreetmap
|
||||
* .org/browser/applications/utils/nominatim/website/search.php featuretype=
|
||||
* to select on feature type (country, city, state, settlement)<br>
|
||||
* format=jsonv2 to get a place_rank<br> offset= to offset the result ?...
|
||||
* <br> polygon=1 to get the border of the poi as a polygon<br> nearlat &
|
||||
* nearlon = ???<br> routewidth/69 and routewidth/30 ???<br> */
|
||||
public static final String MAPQUEST_POI_SERVICE = "http://open.mapquestapi.com/nominatim/v1/";
|
||||
public static final String NOMINATIM_POI_SERVICE = "http://nominatim.openstreetmap.org/";
|
||||
protected String mService;
|
||||
|
||||
public NominatimPOIProvider() {
|
||||
mService = NOMINATIM_POI_SERVICE;
|
||||
}
|
||||
|
||||
public void setService(String serviceUrl) {
|
||||
mService = serviceUrl;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private StringBuffer getCommonUrl(String type, int maxResults) {
|
||||
StringBuffer urlString = new StringBuffer(mService);
|
||||
urlString.append("search?");
|
||||
urlString.append("format=json");
|
||||
urlString.append("&q=" + URLEncoder.encode(type));
|
||||
urlString.append("&limit=" + maxResults);
|
||||
//urlString.append("&bounded=1");
|
||||
// urlString.append("&addressdetails=0");
|
||||
return urlString;
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox bb, String type, int maxResults) {
|
||||
StringBuffer urlString = getCommonUrl(type, maxResults);
|
||||
urlString.append("&viewbox=" + bb.getMaxLongitude() + ","
|
||||
+ bb.getMaxLatitude() + ","
|
||||
+ bb.getMinLongitude() + ","
|
||||
+ bb.getMinLatitude());
|
||||
return urlString.toString();
|
||||
}
|
||||
|
||||
private String getUrlCloseTo(GeoPoint p, String type,
|
||||
int maxResults, double maxDistance) {
|
||||
int maxD = (int) (maxDistance * 1E6);
|
||||
BoundingBox bb = new BoundingBox(p.latitudeE6 + maxD,
|
||||
p.longitudeE6 + maxD,
|
||||
p.latitudeE6 - maxD,
|
||||
p.longitudeE6 - maxD);
|
||||
return getUrlInside(bb, type, maxResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url full URL request
|
||||
* @return the list of POI, of null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getThem(String url) {
|
||||
log.debug("NominatimPOIProvider:get:" + url);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(url);
|
||||
if (jString == null) {
|
||||
log.error("NominatimPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONArray jPlaceIds = new JSONArray(jString);
|
||||
int n = jPlaceIds.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
Bitmap thumbnail = null;
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jPlace = jPlaceIds.getJSONObject(i);
|
||||
POI poi = new POI(POI.POI_SERVICE_NOMINATIM);
|
||||
poi.id = jPlace.getString("osm_id");
|
||||
// jPlace.optLong("osm_id");
|
||||
poi.location = new GeoPoint(jPlace.getDouble("lat"), jPlace.getDouble("lon"));
|
||||
JSONArray bbox = jPlace.optJSONArray("boundingbox");
|
||||
if (bbox != null) {
|
||||
try {
|
||||
poi.bbox = new BoundingBox(bbox.getDouble(0), bbox.getDouble(2),
|
||||
bbox.getDouble(1), bbox.getDouble(3));
|
||||
} catch (Exception e) {
|
||||
log.debug("could not parse " + bbox);
|
||||
}
|
||||
//log.debug("bbox " + poi.bbox);
|
||||
}
|
||||
poi.category = jPlace.optString("class");
|
||||
poi.type = jPlace.getString("type");
|
||||
poi.description = jPlace.optString("display_name");
|
||||
poi.thumbnailPath = jPlace.optString("icon", null);
|
||||
|
||||
if (i == 0 && poi.thumbnailPath != null) {
|
||||
//first POI, and we have a thumbnail: load it
|
||||
thumbnail = BonusPackHelper.loadBitmap(poi.thumbnailPath);
|
||||
}
|
||||
poi.thumbnail = thumbnail;
|
||||
pois.add(poi);
|
||||
}
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position ...
|
||||
* @param type an OpenStreetMap feature. See
|
||||
* http://wiki.openstreetmap.org/wiki/Map_Features or
|
||||
* http://code.google.com/p/osmbonuspack/source/browse/trunk/
|
||||
* OSMBonusPackDemo/res/values/poi_tags.xml
|
||||
* @param maxResults the maximum number of POI returned. Note that in any case,
|
||||
* Nominatim will have an absolute maximum of 100.
|
||||
* @param maxDistance to the position, in degrees. Note that it is used to build a
|
||||
* bounding box around the position, not a circle.
|
||||
* @return the list of POI, null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOICloseTo(GeoPoint position, String type,
|
||||
int maxResults, double maxDistance) {
|
||||
String url = getUrlCloseTo(position, type, maxResults, maxDistance);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox ...
|
||||
* @param type OpenStreetMap feature
|
||||
* @param maxResults ...
|
||||
* @return list of POIs, null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, String type, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, type, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
public ArrayList<POI> getPOI(String query, int maxResults) {
|
||||
String url = getCommonUrl(query, maxResults).toString();
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path Warning: a long path may cause a failure due to the url to be
|
||||
* too long. Using a simplified route may help (see
|
||||
* Road.getRouteLow()).
|
||||
* @param type OpenStreetMap feature
|
||||
* @param maxResults ...
|
||||
* @param maxWidth to the path. Certainly not in degrees. Probably in km.
|
||||
* @return list of POIs, null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIAlong(ArrayList<GeoPoint> path, String type,
|
||||
int maxResults, double maxWidth) {
|
||||
StringBuffer urlString = getCommonUrl(type, maxResults);
|
||||
urlString.append("&routewidth=" + maxWidth);
|
||||
urlString.append("&route=");
|
||||
boolean isFirst = true;
|
||||
for (GeoPoint p : path) {
|
||||
if (isFirst)
|
||||
isFirst = false;
|
||||
else
|
||||
urlString.append(",");
|
||||
String lat = Double.toString(p.getLatitude());
|
||||
lat = lat.substring(0, Math.min(lat.length(), 7));
|
||||
String lon = Double.toString(p.getLongitude());
|
||||
lon = lon.substring(0, Math.min(lon.length(), 7));
|
||||
urlString.append(lat + "," + lon);
|
||||
//limit the size of url as much as possible, as post method is not supported.
|
||||
}
|
||||
return getThem(urlString.toString());
|
||||
}
|
||||
}
|
||||
67
vtm-app/src/org/osmdroid/location/OverpassPOIProvider.java
Normal file
67
vtm-app/src/org/osmdroid/location/OverpassPOIProvider.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.Tag;
|
||||
import org.oscim.core.osm.OsmData;
|
||||
import org.oscim.core.osm.OsmNode;
|
||||
import org.oscim.utils.osmpbf.OsmPbfReader;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OverpassPOIProvider implements POIProvider {
|
||||
|
||||
final static Logger log = LoggerFactory
|
||||
.getLogger(OverpassPOIProvider.class);
|
||||
|
||||
public static final String TAG_KEY_WEBSITE = "website".intern();
|
||||
|
||||
@Override
|
||||
public List<POI> getPOIInside(BoundingBox boundingBox, String query,
|
||||
int maxResults) {
|
||||
HttpConnection connection = new HttpConnection();
|
||||
boundingBox.toString();
|
||||
|
||||
String q = "node[\"amenity\"~\"^restaurant$|^pub$\"]("
|
||||
+ boundingBox.format() + ");out 100;";
|
||||
String url = "http://city.informatik.uni-bremen.de/oapi/pbf?data=";
|
||||
String encoded;
|
||||
try {
|
||||
encoded = URLEncoder.encode(q, "utf-8");
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
e1.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
log.debug("request " + url + encoded);
|
||||
connection.doGet(url + encoded);
|
||||
OsmData osmData = OsmPbfReader.process(connection.getStream());
|
||||
ArrayList<POI> pois = new ArrayList<POI>(osmData.getNodes().size());
|
||||
|
||||
for (OsmNode n : osmData.getNodes()) {
|
||||
POI p = new POI(POI.POI_SERVICE_4SQUARE);
|
||||
p.id = Long.toString(n.id);
|
||||
|
||||
p.location = new GeoPoint(n.lat, n.lon);
|
||||
Tag t;
|
||||
|
||||
if ((t = n.tags.get(Tag.KEY_NAME)) != null)
|
||||
p.description = t.value;
|
||||
|
||||
if ((t = n.tags.get(Tag.KEY_AMENITY)) != null)
|
||||
p.type = t.value;
|
||||
|
||||
if ((t = n.tags.get(TAG_KEY_WEBSITE)) != null) {
|
||||
log.debug(p.description + " " + t.value);
|
||||
p.url = t.value;
|
||||
}
|
||||
pois.add(p);
|
||||
}
|
||||
return pois;
|
||||
}
|
||||
}
|
||||
205
vtm-app/src/org/osmdroid/location/POI.java
Normal file
205
vtm-app/src/org/osmdroid/location/POI.java
Normal file
@@ -0,0 +1,205 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
/**
|
||||
* Point of Interest. Exact content may depend of the POI provider used.
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see NominatimPOIProvider
|
||||
* @see GeoNamesPOIProvider
|
||||
*/
|
||||
public class POI {
|
||||
|
||||
/**
|
||||
* IDs of POI services
|
||||
*/
|
||||
public static int POI_SERVICE_NOMINATIM = 100;
|
||||
public static int POI_SERVICE_GEONAMES_WIKIPEDIA = 200;
|
||||
public static int POI_SERVICE_FLICKR = 300;
|
||||
public static int POI_SERVICE_PICASA = 400;
|
||||
public static int POI_SERVICE_4SQUARE = 500;
|
||||
|
||||
/**
|
||||
* Identifies the service provider of this POI.
|
||||
*/
|
||||
public int serviceId;
|
||||
/**
|
||||
* Nominatim: OSM ID. GeoNames: 0
|
||||
*/
|
||||
public String id;
|
||||
/**
|
||||
* location of the POI
|
||||
*/
|
||||
public GeoPoint location;
|
||||
public BoundingBox bbox;
|
||||
/**
|
||||
* Nominatim "class", GeoNames "feature"
|
||||
*/
|
||||
public String category;
|
||||
/**
|
||||
* type or title
|
||||
*/
|
||||
public String type;
|
||||
/**
|
||||
* can be the name, the address, a short description
|
||||
*/
|
||||
public String description;
|
||||
/**
|
||||
* url of the thumbnail. Null if none
|
||||
*/
|
||||
public String thumbnailPath;
|
||||
/**
|
||||
* the thumbnail itself. Null if none
|
||||
*/
|
||||
public Bitmap thumbnail;
|
||||
/**
|
||||
* url to a more detailed information page about this POI. Null if none
|
||||
*/
|
||||
public String url;
|
||||
/**
|
||||
* popularity of this POI, from 1 (lowest) to 100 (highest). 0 if not
|
||||
* defined.
|
||||
*/
|
||||
public int rank;
|
||||
|
||||
/**
|
||||
* number of attempts to load the thumbnail that have failed
|
||||
*/
|
||||
protected int mThumbnailLoadingFailures;
|
||||
|
||||
public POI(int serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
// lets all other fields empty or null. That's fine.
|
||||
}
|
||||
|
||||
protected static int MAX_LOADING_ATTEMPTS = 2;
|
||||
|
||||
/**
|
||||
* @return the POI thumbnail as a Bitmap, if any. If not done yet, it will
|
||||
* load the POI thumbnail from its url (in thumbnailPath field).
|
||||
*/
|
||||
public Bitmap getThumbnail() {
|
||||
if (thumbnail == null && thumbnailPath != null) {
|
||||
thumbnail = BonusPackHelper.loadBitmap(thumbnailPath);
|
||||
if (thumbnail == null) {
|
||||
mThumbnailLoadingFailures++;
|
||||
if (mThumbnailLoadingFailures >= MAX_LOADING_ATTEMPTS) {
|
||||
// this path really doesn't work, "kill" it for next calls:
|
||||
thumbnailPath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/7729133/using-asynctask-to-load-images-in-listview
|
||||
// TODO see link, there might be a better solution
|
||||
|
||||
/**
|
||||
* Fetch the thumbnail from its url on a thread.
|
||||
*
|
||||
* @param imageView to update once the thumbnail is retrieved, or to hide if no
|
||||
* thumbnail.
|
||||
*/
|
||||
public void fetchThumbnail(final ImageView imageView) {
|
||||
if (thumbnail != null) {
|
||||
imageView.setImageBitmap(thumbnail);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
} else if (thumbnailPath != null) {
|
||||
imageView.setImageResource(R.drawable.ic_empty);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
new ThumbnailTask(imageView).execute(imageView);
|
||||
} else {
|
||||
imageView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
class ThumbnailTask extends AsyncTask<ImageView, Void, ImageView> {
|
||||
|
||||
public ThumbnailTask(ImageView iv) {
|
||||
iv.setTag(thumbnailPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageView doInBackground(ImageView... params) {
|
||||
getThumbnail();
|
||||
return params[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(ImageView iv) {
|
||||
if (iv == null || thumbnail == null)
|
||||
return;
|
||||
if (thumbnailPath.equals(iv.getTag().toString()))
|
||||
iv.setImageBitmap(thumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Parcelable implementation
|
||||
|
||||
// @Override
|
||||
// public int describeContents() {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public void writeToParcel(Parcel out, int flags) {
|
||||
// out.writeInt(serviceId);
|
||||
// out.writeString(id);
|
||||
// out.writeParcelable(location, 0);
|
||||
// out.writeString(category);
|
||||
// out.writeString(type);
|
||||
// out.writeString(description);
|
||||
// out.writeString(thumbnailPath);
|
||||
// out.writeParcelable(thumbnail, 0);
|
||||
// out.writeString(url);
|
||||
// out.writeInt(rank);
|
||||
// out.writeInt(mThumbnailLoadingFailures);
|
||||
// }
|
||||
//
|
||||
// public static final Parcelable.Creator<POI> CREATOR = new Parcelable.Creator<POI>() {
|
||||
// @Override
|
||||
// public POI createFromParcel(Parcel in) {
|
||||
// POI poi = new POI(in.readInt());
|
||||
// poi.id = in.readString();
|
||||
// poi.location = in.readParcelable(GeoPoint.class.getClassLoader());
|
||||
// poi.category = in.readString();
|
||||
// poi.type = in.readString();
|
||||
// poi.description = in.readString();
|
||||
// poi.thumbnailPath = in.readString();
|
||||
// poi.thumbnail = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
// poi.url = in.readString();
|
||||
// poi.rank = in.readInt();
|
||||
// poi.mThumbnailLoadingFailures = in.readInt();
|
||||
// return poi;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public POI[] newArray(int size) {
|
||||
// return new POI[size];
|
||||
// }
|
||||
// };
|
||||
|
||||
// private POI(Parcel in) {
|
||||
// serviceId = in.readInt();
|
||||
// id = in.readLong();
|
||||
// location = in.readParcelable(GeoPoint.class.getClassLoader());
|
||||
// category = in.readString();
|
||||
// type = in.readString();
|
||||
// description = in.readString();
|
||||
// thumbnailPath = in.readString();
|
||||
// thumbnail = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
// url = in.readString();
|
||||
// rank = in.readInt();
|
||||
// mThumbnailLoadingFailures = in.readInt();
|
||||
// }
|
||||
}
|
||||
10
vtm-app/src/org/osmdroid/location/POIProvider.java
Normal file
10
vtm-app/src/org/osmdroid/location/POIProvider.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface POIProvider {
|
||||
|
||||
public List<POI> getPOIInside(BoundingBox boundingBox, String query, int maxResults);
|
||||
}
|
||||
161
vtm-app/src/org/osmdroid/location/PicasaPOIProvider.java
Normal file
161
vtm-app/src/org/osmdroid/location/PicasaPOIProvider.java
Normal file
@@ -0,0 +1,161 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* POI Provider using Picasa service.
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see "https://developers.google.com/picasa-web/docs/2.0/reference"
|
||||
*/
|
||||
public class PicasaPOIProvider implements POIProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(PicasaPOIProvider.class);
|
||||
|
||||
String mAccessToken;
|
||||
|
||||
/**
|
||||
* @param accessToken the account to give to the service. Null for public access.
|
||||
* @see "https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol#CreatingAccount"
|
||||
*/
|
||||
public PicasaPOIProvider(String accessToken) {
|
||||
mAccessToken = accessToken;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private String getUrlInside(BoundingBox boundingBox, int maxResults, String query) {
|
||||
StringBuffer url = new StringBuffer("http://picasaweb.google.com/data/feed/api/all?");
|
||||
url.append("bbox=" + boundingBox.getMinLongitude());
|
||||
url.append("," + boundingBox.getMinLatitude());
|
||||
url.append("," + boundingBox.getMaxLongitude());
|
||||
url.append("," + boundingBox.getMaxLatitude());
|
||||
url.append("&max-results=" + maxResults);
|
||||
url.append("&thumbsize=64c"); //thumbnail size: 64, cropped.
|
||||
url.append("&fields=openSearch:totalResults,entry(summary,media:group/media:thumbnail,media:group/media:title,gphoto:*,georss:where,link)");
|
||||
if (query != null)
|
||||
url.append("&q=" + URLEncoder.encode(query));
|
||||
if (mAccessToken != null) {
|
||||
//TODO: warning: not tested...
|
||||
url.append("&access_token=" + mAccessToken);
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
log.debug("PicasaPOIProvider:get:" + fullUrl);
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(fullUrl);
|
||||
InputStream stream = connection.getStream();
|
||||
if (stream == null) {
|
||||
return null;
|
||||
}
|
||||
PicasaXMLHandler handler = new PicasaXMLHandler();
|
||||
try {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", false);
|
||||
parser.getXMLReader()
|
||||
.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
|
||||
parser.parse(stream, handler);
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
connection.close();
|
||||
if (handler.mPOIs != null)
|
||||
log.debug("done:" + handler.mPOIs.size() + " got on a total of:"
|
||||
+ handler.mTotalResults);
|
||||
return handler.mPOIs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox ...
|
||||
* @param maxResults ...
|
||||
* @param query - optional - full-text query string. Searches the title,
|
||||
* caption and tags for the specified string value.
|
||||
* @return list of POI, Picasa photos inside the bounding box. Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public List<POI> getPOIInside(BoundingBox boundingBox, String query, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, maxResults, query);
|
||||
return getThem(url);
|
||||
}
|
||||
}
|
||||
|
||||
class PicasaXMLHandler extends DefaultHandler {
|
||||
|
||||
private String mString;
|
||||
double mLat, mLng;
|
||||
POI mPOI;
|
||||
ArrayList<POI> mPOIs;
|
||||
int mTotalResults;
|
||||
|
||||
public PicasaXMLHandler() {
|
||||
mPOIs = new ArrayList<POI>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName,
|
||||
Attributes attributes) {
|
||||
if (qName.equals("entry")) {
|
||||
mPOI = new POI(POI.POI_SERVICE_PICASA);
|
||||
} else if (qName.equals("media:thumbnail")) {
|
||||
mPOI.thumbnailPath = attributes.getValue("url");
|
||||
} else if (qName.equals("link")) {
|
||||
String rel = attributes.getValue("rel");
|
||||
if ("http://schemas.google.com/photos/2007#canonical".equals(rel)) {
|
||||
mPOI.url = attributes.getValue("href");
|
||||
}
|
||||
}
|
||||
mString = new String();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
String chars = new String(ch, start, length);
|
||||
mString = mString.concat(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) {
|
||||
if (qName.equals("gml:pos")) {
|
||||
String[] coords = mString.split(" ");
|
||||
mLat = Double.parseDouble(coords[0]);
|
||||
mLng = Double.parseDouble(coords[1]);
|
||||
} else if (qName.equals("gphoto:id")) {
|
||||
mPOI.id = mString;
|
||||
} else if (qName.equals("media:title")) {
|
||||
mPOI.type = mString;
|
||||
} else if (qName.equals("summary")) {
|
||||
mPOI.description = mString;
|
||||
} else if (qName.equals("gphoto:albumtitle")) {
|
||||
mPOI.category = mString;
|
||||
} else if (qName.equals("entry")) {
|
||||
mPOI.location = new GeoPoint(mLat, mLng);
|
||||
mPOIs.add(mPOI);
|
||||
mPOI = null;
|
||||
} else if (qName.equals("openSearch:totalResults")) {
|
||||
mTotalResults = Integer.parseInt(mString);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
99
vtm-app/src/org/osmdroid/overlays/DefaultInfoWindow.java
Normal file
99
vtm-app/src/org/osmdroid/overlays/DefaultInfoWindow.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.oscim.android.MapView;
|
||||
import org.oscim.app.App;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
/**
|
||||
* Default implementation of InfoWindow. It handles a text and a description. It
|
||||
* also handles optionally a sub-description and an image. Clicking on the
|
||||
* bubble will close it.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class DefaultInfoWindow extends InfoWindow {
|
||||
|
||||
// resource ids
|
||||
private static int mTitleId = 0, mDescriptionId = 0, mSubDescriptionId = 0, mImageId = 0;
|
||||
|
||||
private static void setResIds(Context context) {
|
||||
// get application package name
|
||||
String packageName = context.getPackageName();
|
||||
Resources res = context.getResources();
|
||||
|
||||
mTitleId = res.getIdentifier("id/bubble_title", null, packageName);
|
||||
mDescriptionId = res.getIdentifier("id/bubble_description", null, packageName);
|
||||
mSubDescriptionId = res.getIdentifier("id/bubble_subdescription", null, packageName);
|
||||
mImageId = res.getIdentifier("id/bubble_image", null, packageName);
|
||||
|
||||
if (mTitleId == 0 || mDescriptionId == 0) {
|
||||
Log.e(BonusPackHelper.LOG_TAG, "DefaultInfoWindow: unable to get res ids in "
|
||||
+ packageName);
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultInfoWindow(int layoutResId, MapView mapView) {
|
||||
super(layoutResId, mapView);
|
||||
|
||||
if (mTitleId == 0)
|
||||
setResIds(App.activity);
|
||||
|
||||
// default behaviour: close it when clicking on the bubble:
|
||||
mView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ExtendedMarkerItem item) {
|
||||
String title = item.getTitle();
|
||||
if (title == null)
|
||||
title = "";
|
||||
|
||||
((TextView) mView.findViewById(mTitleId)).setText(title);
|
||||
|
||||
String snippet = item.getDescription();
|
||||
if (snippet == null)
|
||||
snippet = "";
|
||||
|
||||
((TextView) mView.findViewById(mDescriptionId)).setText(snippet);
|
||||
|
||||
// handle sub-description, hidding or showing the text view:
|
||||
TextView subDescText = (TextView) mView.findViewById(mSubDescriptionId);
|
||||
String subDesc = item.getSubDescription();
|
||||
if (subDesc != null && !("".equals(subDesc))) {
|
||||
subDescText.setText(subDesc);
|
||||
subDescText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
subDescText.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// handle image
|
||||
ImageView imageView = (ImageView) mView.findViewById(mImageId);
|
||||
Drawable image = item.getImage();
|
||||
if (image != null) {
|
||||
// or setBackgroundDrawable(image)?
|
||||
imageView.setImageDrawable(image);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
} else
|
||||
imageView.setVisibility(View.GONE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
// by default, do nothing
|
||||
}
|
||||
|
||||
}
|
||||
123
vtm-app/src/org/osmdroid/overlays/ExtendedMarkerItem.java
Normal file
123
vtm-app/src/org/osmdroid/overlays/ExtendedMarkerItem.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.layers.marker.MarkerItem;
|
||||
import org.oscim.map.Map;
|
||||
|
||||
/**
|
||||
* An OverlayItem to use in ItemizedOverlayWithBubble<br>
|
||||
* - more complete: can contain an image and a sub-description that will be
|
||||
* displayed in the bubble, <br>
|
||||
* - and flexible: attributes are modifiable<br>
|
||||
* Known Issues:<br>
|
||||
* - Bubble offset is not perfect on h&xhdpi resolutions, due to an osmdroid
|
||||
* issue on marker drawing<br>
|
||||
* - Bubble offset is at 0 when using the default marker => set the marker on
|
||||
* each item!<br>
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see ItemizedOverlayWithBubble
|
||||
*/
|
||||
public class ExtendedMarkerItem extends MarkerItem {
|
||||
|
||||
// now, they are modifiable
|
||||
private String mTitle, mDescription;
|
||||
// now, they are modifiable
|
||||
// a third field that can be displayed in
|
||||
// the infowindow, on a third line
|
||||
// that will be shown in the infowindow.
|
||||
//unfortunately, this is not so simple...
|
||||
private String mSubDescription;
|
||||
private Drawable mImage;
|
||||
private Object mRelatedObject; // reference to an object (of any kind)
|
||||
// linked to this item.
|
||||
|
||||
public ExtendedMarkerItem(String aTitle, String aDescription, GeoPoint aGeoPoint) {
|
||||
super(aTitle, aDescription, aGeoPoint);
|
||||
mTitle = aTitle;
|
||||
mDescription = aDescription;
|
||||
mSubDescription = null;
|
||||
mImage = null;
|
||||
mRelatedObject = null;
|
||||
}
|
||||
|
||||
public void setTitle(String aTitle) {
|
||||
mTitle = aTitle;
|
||||
}
|
||||
|
||||
public void setDescription(String aDescription) {
|
||||
mDescription = aDescription;
|
||||
}
|
||||
|
||||
public void setSubDescription(String aSubDescription) {
|
||||
mSubDescription = aSubDescription;
|
||||
}
|
||||
|
||||
public void setImage(Drawable anImage) {
|
||||
mImage = anImage;
|
||||
}
|
||||
|
||||
public void setRelatedObject(Object o) {
|
||||
mRelatedObject = o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public String getSubDescription() {
|
||||
return mSubDescription;
|
||||
}
|
||||
|
||||
public Drawable getImage() {
|
||||
return mImage;
|
||||
}
|
||||
|
||||
public Object getRelatedObject() {
|
||||
return mRelatedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates this bubble with all item info:
|
||||
* <ul>
|
||||
* title and description in any case,
|
||||
* </ul>
|
||||
* <ul>
|
||||
* image and sub-description if any.
|
||||
* </ul>
|
||||
* and centers the map on the item. <br>
|
||||
*
|
||||
* @param bubble ...
|
||||
* @param map ...
|
||||
*/
|
||||
public void showBubble(InfoWindow bubble, Map map) {
|
||||
// offset the bubble to be top-centered on the marker:
|
||||
// Drawable marker = getMarker(0 /* OverlayItem.ITEM_STATE_FOCUSED_MASK */);
|
||||
// int markerWidth = 0, markerHeight = 0;
|
||||
// if (marker != null) {
|
||||
// markerWidth = marker.getIntrinsicWidth();
|
||||
// markerHeight = marker.getIntrinsicHeight();
|
||||
// } // else... we don't have the default marker size => don't user default
|
||||
// // markers!!!
|
||||
// Point markerH = getHotspot(getMarkerHotspot(), markerWidth, markerHeight);
|
||||
// Point bubbleH = getHotspot(HotspotPlace.TOP_CENTER, markerWidth, markerHeight);
|
||||
// bubbleH.offset(-markerH.x, -markerH.y);
|
||||
//
|
||||
// bubble.open(this, bubbleH.x, bubbleH.y);
|
||||
// OverlayMarker marker = getMarker();
|
||||
// PointF hotspot = marker.getHotspot();
|
||||
// Bitmap b = marker.getBitmap();
|
||||
|
||||
//bubble.open(this, (int)(-b.getWidth() * hotspot.x), (int)(-b.getHeight()));
|
||||
//bubble.open(this, 0, (int)(b.getHeight()));
|
||||
|
||||
bubble.open(this, 0, 0);
|
||||
}
|
||||
}
|
||||
135
vtm-app/src/org/osmdroid/overlays/InfoWindow.java
Normal file
135
vtm-app/src/org/osmdroid/overlays/InfoWindow.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
// TODO composite view as texture overlay and only allow one bubble at a time.
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import org.oscim.android.MapView;
|
||||
|
||||
/**
|
||||
* View that can be displayed on an OSMDroid map, associated to a GeoPoint.
|
||||
* Typical usage: cartoon-like bubbles displayed when clicking an overlay item.
|
||||
* It mimics the InfoWindow class of Google Maps JavaScript API V3. Main
|
||||
* differences are:
|
||||
* <ul>
|
||||
* <li>Structure and content of the view is let to the responsibility of the
|
||||
* caller.</li>
|
||||
* <li>The same InfoWindow can be associated to many items.</li>
|
||||
* </ul>
|
||||
* Known issues:
|
||||
* <ul>
|
||||
* <li>It disappears when zooming in/out (osmdroid issue #259 on osmdroid 3.0.8,
|
||||
* should be fixed in next version).</li>
|
||||
* <li>The window is displayed "above" the marker, so the queue of the bubble
|
||||
* can hide the marker.</li>
|
||||
* </ul>
|
||||
* This is an abstract class.
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see DefaultInfoWindow
|
||||
*/
|
||||
public abstract class InfoWindow {
|
||||
|
||||
protected View mView;
|
||||
protected boolean mIsVisible = false;
|
||||
protected RelativeLayout mLayout;
|
||||
private android.widget.RelativeLayout.LayoutParams mLayoutPos;
|
||||
|
||||
private MapView mMap;
|
||||
|
||||
/**
|
||||
* @param layoutResId the id of the view resource.
|
||||
* @param mapView the mapview on which is hooked the view
|
||||
*/
|
||||
public InfoWindow(int layoutResId, MapView mapView) {
|
||||
ViewGroup parent = (ViewGroup) mapView.getParent();
|
||||
Context context = mapView.getContext();
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mView = inflater.inflate(layoutResId, parent, false);
|
||||
|
||||
RelativeLayout.LayoutParams rlp =
|
||||
new RelativeLayout.LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
mLayout = new RelativeLayout(context);
|
||||
mLayout.setWillNotDraw(true);
|
||||
mLayout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
|
||||
mLayout.setLayoutParams(rlp);
|
||||
mLayoutPos = rlp;
|
||||
mView.setDrawingCacheEnabled(true);
|
||||
mLayout.addView(mView);
|
||||
|
||||
mIsVisible = false;
|
||||
mLayout.setVisibility(View.GONE);
|
||||
mMap = mapView;
|
||||
|
||||
parent.addView(mLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Android view. This allows to set its content.
|
||||
*
|
||||
* @return the Android view
|
||||
*/
|
||||
public View getView() {
|
||||
return (mView);
|
||||
}
|
||||
|
||||
private int mHeight;
|
||||
|
||||
/**
|
||||
* open the window at the specified position.
|
||||
*
|
||||
* @param item the item on which is hooked the view
|
||||
* @param offsetX (&offsetY) the offset of the view to the position, in pixels.
|
||||
* This allows to offset the view from the marker position.
|
||||
* @param offsetY ...
|
||||
*/
|
||||
public void open(ExtendedMarkerItem item, int offsetX, int offsetY) {
|
||||
|
||||
onOpen(item);
|
||||
close();
|
||||
|
||||
mView.buildDrawingCache();
|
||||
|
||||
mHeight = mMap.getHeight();
|
||||
mLayout.setVisibility(View.VISIBLE);
|
||||
mIsVisible = true;
|
||||
|
||||
}
|
||||
|
||||
public void position(int x, int y) {
|
||||
RelativeLayout.LayoutParams rlp = mLayoutPos;
|
||||
rlp.leftMargin = x;
|
||||
rlp.rightMargin = -x;
|
||||
rlp.topMargin = y;
|
||||
rlp.bottomMargin = mHeight / 2 - y;
|
||||
mLayout.setLayoutParams(rlp);
|
||||
mLayout.requestLayout();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
|
||||
if (mIsVisible) {
|
||||
mIsVisible = false;
|
||||
mLayout.setVisibility(View.GONE);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return mIsVisible;
|
||||
}
|
||||
|
||||
// Abstract methods to implement:
|
||||
public abstract void onOpen(ExtendedMarkerItem item);
|
||||
|
||||
public abstract void onClose();
|
||||
|
||||
}
|
||||
195
vtm-app/src/org/osmdroid/overlays/ItemizedOverlayWithBubble.java
Normal file
195
vtm-app/src/org/osmdroid/overlays/ItemizedOverlayWithBubble.java
Normal file
@@ -0,0 +1,195 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.oscim.app.App;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Point;
|
||||
import org.oscim.event.Event;
|
||||
import org.oscim.event.MotionEvent;
|
||||
import org.oscim.layers.marker.ItemizedLayer;
|
||||
import org.oscim.layers.marker.MarkerItem;
|
||||
import org.oscim.layers.marker.MarkerSymbol;
|
||||
import org.oscim.map.Map;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An itemized overlay with an InfoWindow or "bubble" which opens when the user
|
||||
* taps on an overlay item, and displays item attributes. <br>
|
||||
* Items must be ExtendedMarkerItem. <br>
|
||||
*
|
||||
* @param <Item> ...
|
||||
* @author M.Kergall
|
||||
* @see ExtendedMarkerItem
|
||||
* @see InfoWindow
|
||||
*/
|
||||
public class ItemizedOverlayWithBubble<Item extends MarkerItem> extends
|
||||
ItemizedLayer<Item> implements
|
||||
ItemizedLayer.OnItemGestureListener<Item>, Map.UpdateListener {
|
||||
|
||||
/* only one for all items of this overlay => one at a time */
|
||||
protected InfoWindow mBubble;
|
||||
|
||||
/* the item currently showing the bubble. Null if none. */
|
||||
protected MarkerItem mItemWithBubble;
|
||||
|
||||
static int layoutResId = 0;
|
||||
|
||||
public ItemizedOverlayWithBubble(Map map, Context context,
|
||||
MarkerSymbol marker, List<Item> list, InfoWindow bubble) {
|
||||
super(map, list, marker, null);
|
||||
|
||||
if (bubble != null) {
|
||||
mBubble = bubble;
|
||||
} else {
|
||||
// build default bubble:
|
||||
String packageName = context.getPackageName();
|
||||
if (layoutResId == 0) {
|
||||
layoutResId = context.getResources().getIdentifier(
|
||||
"layout/bonuspack_bubble",
|
||||
null,
|
||||
packageName);
|
||||
if (layoutResId == 0)
|
||||
Log.e(BonusPackHelper.LOG_TAG,
|
||||
"ItemizedOverlayWithBubble: layout/bonuspack_bubble not found in "
|
||||
+ packageName);
|
||||
}
|
||||
mBubble = new DefaultInfoWindow(layoutResId, App.view);
|
||||
}
|
||||
|
||||
mItemWithBubble = null;
|
||||
mOnItemGestureListener = this;
|
||||
}
|
||||
|
||||
public ItemizedOverlayWithBubble(Map map, Context context,
|
||||
MarkerSymbol marker, List<Item> aList) {
|
||||
this(map, context, marker, aList, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongPress(int index, MarkerItem item) {
|
||||
if (mBubble.isOpen())
|
||||
hideBubble();
|
||||
else
|
||||
showBubble(index);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemSingleTapUp(int index, MarkerItem item) {
|
||||
showBubble(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final Point mTmpPoint = new Point();
|
||||
|
||||
@Override
|
||||
protected boolean activateSelectedItems(MotionEvent event, ActiveItem task) {
|
||||
boolean hit = super.activateSelectedItems(event, task);
|
||||
|
||||
if (!hit)
|
||||
hideBubble();
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapEvent(Event e, MapPosition mapPosition) {
|
||||
if (mBubble.isOpen()) {
|
||||
GeoPoint gp = mItemWithBubble.getPoint();
|
||||
|
||||
Point p = mTmpPoint;
|
||||
mMap.viewport().toScreenPoint(gp, p);
|
||||
|
||||
mBubble.position((int) p.x, (int) p.y);
|
||||
}
|
||||
}
|
||||
|
||||
void showBubble(int index) {
|
||||
showBubbleOnItem(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the bubble on the item. For each ItemizedOverlay, only one bubble
|
||||
* is opened at a time. If you want more bubbles opened simultaneously, use
|
||||
* many ItemizedOverlays.
|
||||
*
|
||||
* @param index of the overlay item to show
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void showBubbleOnItem(int index) {
|
||||
ExtendedMarkerItem item = (ExtendedMarkerItem) (mItemList.get(index));
|
||||
mItemWithBubble = item;
|
||||
if (item != null) {
|
||||
item.showBubble(mBubble, (Map) mMap);
|
||||
|
||||
mMap.animator().animateTo(item.geoPoint);
|
||||
|
||||
mMap.updateMap(true);
|
||||
setFocus((Item) item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the bubble (if it's opened).
|
||||
*/
|
||||
public void hideBubble() {
|
||||
mBubble.close();
|
||||
mItemWithBubble = null;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean onSingleTapUp(final MotionEvent event) {
|
||||
// boolean handled = super.onSingleTapUp(event);
|
||||
// if (!handled)
|
||||
// hideBubble();
|
||||
// return handled;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected boolean onSingleTapUpHelper(final int index, final Item item) {
|
||||
// showBubbleOnItem(index);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
/**
|
||||
* @return the item currenty showing the bubble, or null if none.
|
||||
*/
|
||||
public MarkerItem getBubbledItem() {
|
||||
if (mBubble.isOpen())
|
||||
return mItemWithBubble;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the index of the item currenty showing the bubble, or -1 if none.
|
||||
*/
|
||||
public int getBubbledItemId() {
|
||||
MarkerItem item = getBubbledItem();
|
||||
if (item == null)
|
||||
return -1;
|
||||
|
||||
return mItemList.indexOf(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeItem(final Item item) {
|
||||
boolean result = super.removeItem(item);
|
||||
if (mItemWithBubble == item) {
|
||||
hideBubble();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllItems() {
|
||||
super.removeAllItems();
|
||||
hideBubble();
|
||||
}
|
||||
}
|
||||
34
vtm-app/src/org/osmdroid/overlays/MapEventsReceiver.java
Normal file
34
vtm-app/src/org/osmdroid/overlays/MapEventsReceiver.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
/**
|
||||
* Interface for objects that need to handle map events thrown by a
|
||||
* MapEventsOverlay.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public interface MapEventsReceiver {
|
||||
|
||||
/**
|
||||
* @param p the position where the event occurred.
|
||||
* @return true if the event has been "consumed" and should not be handled
|
||||
* by other objects.
|
||||
*/
|
||||
boolean singleTapUpHelper(GeoPoint p);
|
||||
|
||||
/**
|
||||
* @param p the position where the event occurred.
|
||||
* @return true if the event has been "consumed" and should not be handled
|
||||
* by other objects.
|
||||
*/
|
||||
boolean longPressHelper(GeoPoint p);
|
||||
|
||||
/**
|
||||
* @param p1 p2
|
||||
* the position where the event occurred for 2 finger.
|
||||
* @return true if the event has been "consumed" and should not be handled
|
||||
* by other objects.
|
||||
*/
|
||||
boolean longPressHelper(GeoPoint p1, GeoPoint p2);
|
||||
}
|
||||
255
vtm-app/src/org/osmdroid/routing/Route.java
Normal file
255
vtm-app/src/org/osmdroid/routing/Route.java
Normal file
@@ -0,0 +1,255 @@
|
||||
package org.osmdroid.routing;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.routing.provider.GoogleRouteProvider;
|
||||
import org.osmdroid.routing.provider.MapQuestRouteProvider;
|
||||
import org.osmdroid.routing.provider.OSRMRouteProvider;
|
||||
import org.osmdroid.utils.DouglasPeuckerReducer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* describes the way to go from a position to an other. Normally returned by a
|
||||
* call to a Directions API (from MapQuest, GoogleMaps or other)
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see MapQuestRouteProvider
|
||||
* @see GoogleRouteProvider
|
||||
* @see OSRMRouteProvider
|
||||
*/
|
||||
public class Route {
|
||||
//final static Logger log = LoggerFactory.getLogger(Route.class);
|
||||
|
||||
/**
|
||||
* @see #STATUS_INVALID STATUS_INVALID
|
||||
* @see #STATUS_OK STATUS_OK
|
||||
* @see #STATUS_DEFAULT STATUS_DEFAULT
|
||||
*/
|
||||
public int status;
|
||||
|
||||
/**
|
||||
* length of the whole route in km.
|
||||
*/
|
||||
public double length;
|
||||
/**
|
||||
* duration of the whole trip in sec.
|
||||
*/
|
||||
public double duration;
|
||||
public List<RouteNode> nodes;
|
||||
/** */
|
||||
/**
|
||||
* there is one leg between each waypoint
|
||||
*/
|
||||
public List<RouteLeg> legs;
|
||||
/**
|
||||
* full shape: polyline, as an array of GeoPoints
|
||||
*/
|
||||
public List<GeoPoint> routeHigh;
|
||||
/**
|
||||
* the same, in low resolution (less points)
|
||||
*/
|
||||
private List<GeoPoint> routeLow;
|
||||
/**
|
||||
* route bounding box
|
||||
*/
|
||||
public BoundingBox boundingBox;
|
||||
|
||||
/**
|
||||
* STATUS_INVALID = route not built
|
||||
*/
|
||||
public static final int STATUS_INVALID = 0;
|
||||
/**
|
||||
* STATUS_OK = route properly retrieved and built
|
||||
*/
|
||||
public static final int STATUS_OK = 1;
|
||||
/**
|
||||
* STATUS_DEFAULT = any issue (technical issue, or no possible route) led to
|
||||
* build a default route
|
||||
*/
|
||||
public static final int STATUS_DEFAULT = 2;
|
||||
|
||||
private void init() {
|
||||
status = STATUS_INVALID;
|
||||
length = 0.0;
|
||||
duration = 0.0;
|
||||
nodes = new ArrayList<RouteNode>();
|
||||
routeHigh = new ArrayList<GeoPoint>();
|
||||
routeLow = null;
|
||||
legs = new ArrayList<RouteLeg>();
|
||||
boundingBox = null;
|
||||
}
|
||||
|
||||
public Route() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* default constructor when normal loading failed: the route shape only
|
||||
* contains the waypoints; All distances and times are at 0; there is no
|
||||
* node; status equals DEFAULT.
|
||||
*
|
||||
* @param waypoints ...
|
||||
*/
|
||||
public Route(List<GeoPoint> waypoints) {
|
||||
init();
|
||||
int n = waypoints.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
GeoPoint p = waypoints.get(i);
|
||||
routeHigh.add(p);
|
||||
}
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
RouteLeg leg = new RouteLeg(/* i, i+1, mLinks */);
|
||||
legs.add(leg);
|
||||
}
|
||||
boundingBox = BoundingBox.fromGeoPoints(routeHigh);
|
||||
status = STATUS_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the route shape in "low resolution" = simplified by around 10
|
||||
* factor.
|
||||
*/
|
||||
public List<GeoPoint> getRouteLow() {
|
||||
if (routeLow == null) {
|
||||
// Simplify the route (divide number of points by around 10):
|
||||
//int n = routeHigh.size();
|
||||
routeLow = DouglasPeuckerReducer.reduceWithTolerance(routeHigh, 1500.0);
|
||||
//log.debug("route reduced from " + n + " to " + routeLow.size()
|
||||
// + " points");
|
||||
}
|
||||
return routeLow;
|
||||
}
|
||||
|
||||
public void setRouteLow(ArrayList<GeoPoint> route) {
|
||||
routeLow = route;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pLength in km
|
||||
* @param pDuration in sec
|
||||
* @return a human-readable length&duration text.
|
||||
*/
|
||||
public String getLengthDurationText(double pLength, double pDuration) {
|
||||
String result;
|
||||
if (pLength >= 100.0) {
|
||||
result = (int) (pLength) + " km, ";
|
||||
} else if (pLength >= 1.0) {
|
||||
result = Math.round(pLength * 10) / 10.0 + " km, ";
|
||||
} else {
|
||||
result = (int) (pLength * 1000) + " m, ";
|
||||
}
|
||||
int totalSeconds = (int) pDuration;
|
||||
int hours = totalSeconds / 3600;
|
||||
int minutes = (totalSeconds / 60) - (hours * 60);
|
||||
int seconds = (totalSeconds % 60);
|
||||
if (hours != 0) {
|
||||
result += hours + " h ";
|
||||
}
|
||||
if (minutes != 0) {
|
||||
result += minutes + " min";
|
||||
}
|
||||
if (hours == 0 && minutes == 0) {
|
||||
result += seconds + " s";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param leg leg index, starting from 0. -1 for the whole route
|
||||
* @return length and duration of the whole route, or of a leg of the route,
|
||||
* as a String, in a readable format.
|
||||
*/
|
||||
public String getLengthDurationText(int leg) {
|
||||
double len = (leg == -1 ? this.length : legs.get(leg).length);
|
||||
double dur = (leg == -1 ? this.duration : legs.get(leg).duration);
|
||||
return getLengthDurationText(len, dur);
|
||||
}
|
||||
|
||||
protected double distanceLLSquared(GeoPoint p1, GeoPoint p2) {
|
||||
double deltaLat = p2.latitudeE6 - p1.latitudeE6;
|
||||
double deltaLon = p2.longitudeE6 - p1.longitudeE6;
|
||||
return (deltaLat * deltaLat + deltaLon * deltaLon);
|
||||
}
|
||||
|
||||
/**
|
||||
* As MapQuest and OSRM doesn't provide legs information, we have to rebuild
|
||||
* it, using the waypoints and the route nodes. <br>
|
||||
* Note that MapQuest legs fit well with waypoints, as there is a
|
||||
* "dedicated" node for each waypoint. But OSRM legs are not precise, as
|
||||
* there is no node "dedicated" to waypoints.
|
||||
*
|
||||
* @param waypoints ...
|
||||
*/
|
||||
public void buildLegs(List<GeoPoint> waypoints) {
|
||||
legs = new ArrayList<RouteLeg>();
|
||||
int firstNodeIndex = 0;
|
||||
// For all intermediate waypoints, search the node closest to the
|
||||
// waypoint
|
||||
int w = waypoints.size();
|
||||
int n = nodes.size();
|
||||
for (int i = 1; i < w - 1; i++) {
|
||||
GeoPoint waypoint = waypoints.get(i);
|
||||
double distanceMin = -1.0;
|
||||
int nodeIndexMin = -1;
|
||||
for (int j = firstNodeIndex; j < n; j++) {
|
||||
GeoPoint routePoint = nodes.get(j).location;
|
||||
double dSquared = distanceLLSquared(routePoint, waypoint);
|
||||
if (nodeIndexMin == -1 || dSquared < distanceMin) {
|
||||
distanceMin = dSquared;
|
||||
nodeIndexMin = j;
|
||||
}
|
||||
}
|
||||
// Build the leg as ending with this closest node:
|
||||
RouteLeg leg = new RouteLeg(firstNodeIndex, nodeIndexMin, nodes);
|
||||
legs.add(leg);
|
||||
firstNodeIndex = nodeIndexMin + 1; // restart next leg from end
|
||||
}
|
||||
// Build last leg ending with last node:
|
||||
RouteLeg lastLeg = new RouteLeg(firstNodeIndex, n - 1, nodes);
|
||||
legs.add(lastLeg);
|
||||
}
|
||||
|
||||
// --- Parcelable implementation
|
||||
|
||||
// @Override
|
||||
// public int describeContents() {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void writeToParcel(Parcel out, int flags) {
|
||||
// out.writeInt(status);
|
||||
// out.writeDouble(length);
|
||||
// out.writeDouble(duration);
|
||||
// out.writeList(nodes);
|
||||
// out.writeList(legs);
|
||||
// out.writeList(routeHigh);
|
||||
// out.writeParcelable(boundingBox, 0);
|
||||
// }
|
||||
//
|
||||
// public static final Parcelable.Creator<Route> CREATOR = new Parcelable.Creator<Route>() {
|
||||
// @Override
|
||||
// public Route createFromParcel(Parcel source) {
|
||||
// return new Route(source);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Route[] newArray(int size) {
|
||||
// return new Route[size];
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private Route(Parcel in) {
|
||||
// status = in.readInt();
|
||||
// length = in.readDouble();
|
||||
// duration = in.readDouble();
|
||||
//
|
||||
// nodes = in.readArrayList(RouteNode.class.getClassLoader());
|
||||
// legs = in.readArrayList(RouteLeg.class.getClassLoader());
|
||||
// routeHigh = in.readArrayList(GeoPoint.class.getClassLoader());
|
||||
// boundingBox = in.readParcelable(BoundingBox.class.getClassLoader());
|
||||
// }
|
||||
}
|
||||
85
vtm-app/src/org/osmdroid/routing/RouteLeg.java
Normal file
85
vtm-app/src/org/osmdroid/routing/RouteLeg.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package org.osmdroid.routing;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Road Leg is the portion of the route between 2 waypoints (intermediate points
|
||||
* requested)
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class RouteLeg implements Parcelable {
|
||||
//final static Logger log = LoggerFactory.getLogger(RouteLeg.class);
|
||||
|
||||
/**
|
||||
* in km
|
||||
*/
|
||||
public double length;
|
||||
/**
|
||||
* in sec
|
||||
*/
|
||||
public double duration;
|
||||
/**
|
||||
* starting node of the leg, as index in nodes array
|
||||
*/
|
||||
public int startNodeIndex;
|
||||
/**
|
||||
* and ending node
|
||||
*/
|
||||
public int endNodeIndex;
|
||||
|
||||
public RouteLeg() {
|
||||
length = duration = 0.0;
|
||||
startNodeIndex = endNodeIndex = 0;
|
||||
}
|
||||
|
||||
public RouteLeg(int startNodeIndex, int endNodeIndex,
|
||||
List<RouteNode> nodes) {
|
||||
this.startNodeIndex = startNodeIndex;
|
||||
this.endNodeIndex = endNodeIndex;
|
||||
length = duration = 0.0;
|
||||
|
||||
for (int i = startNodeIndex; i <= endNodeIndex; i++) {
|
||||
RouteNode node = nodes.get(i);
|
||||
length += node.length;
|
||||
duration += node.duration;
|
||||
}
|
||||
//log.debug("Leg: " + startNodeIndex + "-" + endNodeIndex
|
||||
// + ", length=" + length + "km, duration=" + duration + "s");
|
||||
}
|
||||
|
||||
//--- Parcelable implementation
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeDouble(length);
|
||||
out.writeDouble(duration);
|
||||
out.writeInt(startNodeIndex);
|
||||
out.writeInt(endNodeIndex);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<RouteLeg> CREATOR = new Parcelable.Creator<RouteLeg>() {
|
||||
@Override
|
||||
public RouteLeg createFromParcel(Parcel in) {
|
||||
RouteLeg rl = new RouteLeg();
|
||||
rl.length = in.readDouble();
|
||||
rl.duration = in.readDouble();
|
||||
rl.startNodeIndex = in.readInt();
|
||||
rl.endNodeIndex = in.readInt();
|
||||
return rl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RouteLeg[] newArray(int size) {
|
||||
return new RouteLeg[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
79
vtm-app/src/org/osmdroid/routing/RouteNode.java
Normal file
79
vtm-app/src/org/osmdroid/routing/RouteNode.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package org.osmdroid.routing;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
/**
|
||||
* Route intersection, with instructions to continue.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class RouteNode {
|
||||
/**
|
||||
* @see <a
|
||||
* href="http://open.mapquestapi.com/guidance/#maneuvertypes">Maneuver
|
||||
* Types</a>
|
||||
*/
|
||||
public int maneuverType;
|
||||
/**
|
||||
* textual information on what to do at this intersection
|
||||
*/
|
||||
public String instructions;
|
||||
/**
|
||||
* index in route links array - internal use only, for MapQuest directions
|
||||
*/
|
||||
public int nextRouteLink;
|
||||
/**
|
||||
* in km to the next node
|
||||
*/
|
||||
public double length;
|
||||
/**
|
||||
* in seconds to the next node
|
||||
*/
|
||||
public double duration;
|
||||
/**
|
||||
* position of the node
|
||||
*/
|
||||
public GeoPoint location;
|
||||
|
||||
public RouteNode() {
|
||||
maneuverType = 0;
|
||||
nextRouteLink = -1;
|
||||
length = duration = 0.0;
|
||||
}
|
||||
|
||||
// --- Parcelable implementation
|
||||
|
||||
// @Override
|
||||
// public int describeContents() {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void writeToParcel(Parcel out, int flags) {
|
||||
// out.writeInt(maneuverType);
|
||||
// out.writeString(instructions);
|
||||
// out.writeDouble(length);
|
||||
// out.writeDouble(duration);
|
||||
// out.writeParcelable(location, 0);
|
||||
// }
|
||||
//
|
||||
// public static final Parcelable.Creator<RouteNode> CREATOR = new
|
||||
// Parcelable.Creator<RouteNode>() {
|
||||
// @Override
|
||||
// public RouteNode createFromParcel(Parcel in) {
|
||||
// RouteNode rn = new RouteNode();
|
||||
// rn.maneuverType = in.readInt();
|
||||
// rn.instructions = in.readString();
|
||||
// rn.length = in.readDouble();
|
||||
// rn.duration = in.readDouble();
|
||||
// rn.location = in.readParcelable(GeoPoint.class.getClassLoader());
|
||||
// return rn;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public RouteNode[] newArray(int size) {
|
||||
// return new RouteNode[size];
|
||||
// }
|
||||
// };
|
||||
|
||||
}
|
||||
67
vtm-app/src/org/osmdroid/routing/RouteProvider.java
Normal file
67
vtm-app/src/org/osmdroid/routing/RouteProvider.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package org.osmdroid.routing;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.layers.PathLayer;
|
||||
import org.oscim.map.Map;
|
||||
import org.osmdroid.routing.provider.GoogleRouteProvider;
|
||||
import org.osmdroid.routing.provider.MapQuestRouteProvider;
|
||||
import org.osmdroid.routing.provider.OSRMRouteProvider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Generic class to get a route between a start and a destination point, going
|
||||
* through a list of waypoints.
|
||||
*
|
||||
* @author M.Kergall
|
||||
* @see MapQuestRouteProvider
|
||||
* @see GoogleRouteProvider
|
||||
* @see OSRMRouteProvider
|
||||
*/
|
||||
public abstract class RouteProvider {
|
||||
|
||||
protected String mOptions;
|
||||
|
||||
public abstract Route getRoute(List<GeoPoint> waypoints);
|
||||
|
||||
public RouteProvider() {
|
||||
mOptions = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an option that will be used in the route request. Note that some
|
||||
* options are set in the request in all cases.
|
||||
*
|
||||
* @param requestOption see provider documentation. Just one example:
|
||||
* "routeType=bicycle" for MapQuest; "mode=bicycling" for Google.
|
||||
*/
|
||||
public void addRequestOption(String requestOption) {
|
||||
mOptions += "&" + requestOption;
|
||||
}
|
||||
|
||||
protected String geoPointAsString(GeoPoint p) {
|
||||
StringBuffer result = new StringBuffer();
|
||||
double d = p.getLatitude();
|
||||
result.append(Double.toString(d));
|
||||
d = p.getLongitude();
|
||||
result.append("," + Double.toString(d));
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an overlay for the route shape with a default (and nice!) color.
|
||||
*
|
||||
* @return route shape overlay
|
||||
*/
|
||||
public static PathLayer buildRouteOverlay(Map map, Route route) {
|
||||
int lineColor = 0x800000FF;
|
||||
float lineWidth = 2.5f;
|
||||
|
||||
PathLayer routeOverlay = new PathLayer(map, lineColor, lineWidth);
|
||||
if (route != null) {
|
||||
routeOverlay.setPoints(route.routeHigh);
|
||||
}
|
||||
return routeOverlay;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package org.osmdroid.routing.provider;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.routing.Route;
|
||||
import org.osmdroid.routing.RouteLeg;
|
||||
import org.osmdroid.routing.RouteNode;
|
||||
import org.osmdroid.routing.RouteProvider;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.osmdroid.utils.PolylineEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* class to get a route between a start and a destination point, going through a
|
||||
* list of waypoints. <br>
|
||||
* https://developers.google.com/maps/documentation/directions/<br>
|
||||
* Note that displaying a route provided by Google on a non-Google map (like
|
||||
* OSM) is not allowed by Google T&C.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class GoogleRouteProvider extends RouteProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(GoogleRouteProvider.class);
|
||||
|
||||
static final String GOOGLE_DIRECTIONS_SERVICE = "http://maps.googleapis.com/maps/api/directions/xml?";
|
||||
|
||||
/**
|
||||
* Build the URL to Google Directions service returning a route in XML
|
||||
* format
|
||||
*
|
||||
* @param waypoints ...
|
||||
* @return ...
|
||||
*/
|
||||
protected String getUrl(List<GeoPoint> waypoints) {
|
||||
StringBuffer urlString = new StringBuffer(GOOGLE_DIRECTIONS_SERVICE);
|
||||
urlString.append("origin=");
|
||||
GeoPoint p = waypoints.get(0);
|
||||
urlString.append(geoPointAsString(p));
|
||||
urlString.append("&destination=");
|
||||
int destinationIndex = waypoints.size() - 1;
|
||||
p = waypoints.get(destinationIndex);
|
||||
urlString.append(geoPointAsString(p));
|
||||
|
||||
for (int i = 1; i < destinationIndex; i++) {
|
||||
if (i == 1)
|
||||
urlString.append("&waypoints=");
|
||||
else
|
||||
urlString.append("%7C"); // the pipe (|), url-encoded
|
||||
p = waypoints.get(i);
|
||||
urlString.append(geoPointAsString(p));
|
||||
}
|
||||
urlString.append("&units=metric&sensor=false");
|
||||
Locale locale = Locale.getDefault();
|
||||
urlString.append("&language=" + locale.getLanguage());
|
||||
urlString.append(mOptions);
|
||||
return urlString.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param waypoints : list of GeoPoints. Must have at least 2 entries, start and
|
||||
* end points.
|
||||
* @return the route
|
||||
*/
|
||||
@Override
|
||||
public Route getRoute(List<GeoPoint> waypoints) {
|
||||
String url = getUrl(waypoints);
|
||||
log.debug("GoogleRouteManager.getRoute:" + url);
|
||||
Route route = null;
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(url);
|
||||
InputStream stream = connection.getStream();
|
||||
if (stream != null)
|
||||
route = getRouteXML(stream);
|
||||
connection.close();
|
||||
if (route == null || route.routeHigh.size() == 0) {
|
||||
//Create default route:
|
||||
route = new Route(waypoints);
|
||||
} else {
|
||||
//finalize route data update:
|
||||
for (RouteLeg leg : route.legs) {
|
||||
route.duration += leg.duration;
|
||||
route.length += leg.length;
|
||||
}
|
||||
route.status = Route.STATUS_OK;
|
||||
}
|
||||
log.debug("GoogleRouteManager.getRoute - finished");
|
||||
return route;
|
||||
}
|
||||
|
||||
protected Route getRouteXML(InputStream is) {
|
||||
GoogleDirectionsHandler handler = new GoogleDirectionsHandler();
|
||||
try {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(is, handler);
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return handler.mRoute;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GoogleDirectionsHandler extends DefaultHandler {
|
||||
Route mRoute;
|
||||
RouteLeg mLeg;
|
||||
RouteNode mNode;
|
||||
boolean isPolyline, isOverviewPolyline, isLeg, isStep, isDuration, isDistance, isBB;
|
||||
int mValue;
|
||||
double mLat, mLng;
|
||||
double mNorth, mWest, mSouth, mEast;
|
||||
private String mString;
|
||||
|
||||
public GoogleDirectionsHandler() {
|
||||
isOverviewPolyline = isBB = isPolyline = isLeg = isStep = isDuration = isDistance = false;
|
||||
mRoute = new Route();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String name,
|
||||
Attributes attributes) {
|
||||
if (localName.equals("polyline")) {
|
||||
isPolyline = true;
|
||||
} else if (localName.equals("overview_polyline")) {
|
||||
isOverviewPolyline = true;
|
||||
} else if (localName.equals("leg")) {
|
||||
mLeg = new RouteLeg();
|
||||
isLeg = true;
|
||||
} else if (localName.equals("step")) {
|
||||
mNode = new RouteNode();
|
||||
isStep = true;
|
||||
} else if (localName.equals("duration")) {
|
||||
isDuration = true;
|
||||
} else if (localName.equals("distance")) {
|
||||
isDistance = true;
|
||||
} else if (localName.equals("bounds")) {
|
||||
isBB = true;
|
||||
}
|
||||
mString = new String();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
|
||||
*/
|
||||
public
|
||||
@Override
|
||||
void characters(char[] ch, int start, int length) {
|
||||
String chars = new String(ch, start, length);
|
||||
mString = mString.concat(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String name) {
|
||||
if (localName.equals("points")) {
|
||||
if (isPolyline) {
|
||||
//detailed piece of route for the step, to add:
|
||||
ArrayList<GeoPoint> polyLine = PolylineEncoder.decode(mString, 10);
|
||||
mRoute.routeHigh.addAll(polyLine);
|
||||
} else if (isOverviewPolyline) {
|
||||
//low-def polyline for the whole route:
|
||||
mRoute.setRouteLow(PolylineEncoder.decode(mString, 10));
|
||||
}
|
||||
} else if (localName.equals("polyline")) {
|
||||
isPolyline = false;
|
||||
} else if (localName.equals("overview_polyline")) {
|
||||
isOverviewPolyline = false;
|
||||
} else if (localName.equals("value")) {
|
||||
mValue = Integer.parseInt(mString);
|
||||
} else if (localName.equals("duration")) {
|
||||
if (isStep)
|
||||
mNode.duration = mValue;
|
||||
else
|
||||
mLeg.duration = mValue;
|
||||
isDuration = false;
|
||||
} else if (localName.equals("distance")) {
|
||||
if (isStep)
|
||||
mNode.length = mValue / 1000.0;
|
||||
else
|
||||
mLeg.length = mValue / 1000.0;
|
||||
isDistance = false;
|
||||
} else if (localName.equals("html_instructions")) {
|
||||
if (isStep) {
|
||||
mString = mString.replaceAll("<[^>]*>", " "); //remove everything in <...>
|
||||
mString = mString.replaceAll(" ", " ");
|
||||
mNode.instructions = mString;
|
||||
//log.debug(mString);
|
||||
}
|
||||
} else if (localName.equals("start_location")) {
|
||||
if (isStep)
|
||||
mNode.location = new GeoPoint(mLat, mLng);
|
||||
} else if (localName.equals("step")) {
|
||||
mRoute.nodes.add(mNode);
|
||||
isStep = false;
|
||||
} else if (localName.equals("leg")) {
|
||||
mRoute.legs.add(mLeg);
|
||||
isLeg = false;
|
||||
} else if (localName.equals("lat")) {
|
||||
mLat = Double.parseDouble(mString);
|
||||
} else if (localName.equals("lng")) {
|
||||
mLng = Double.parseDouble(mString);
|
||||
} else if (localName.equals("northeast")) {
|
||||
if (isBB) {
|
||||
mNorth = mLat;
|
||||
mEast = mLng;
|
||||
}
|
||||
} else if (localName.equals("southwest")) {
|
||||
if (isBB) {
|
||||
mSouth = mLat;
|
||||
mWest = mLng;
|
||||
}
|
||||
} else if (localName.equals("bounds")) {
|
||||
mRoute.boundingBox = new BoundingBox(mNorth, mEast, mSouth, mWest);
|
||||
isBB = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package org.osmdroid.routing.provider;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.routing.Route;
|
||||
import org.osmdroid.routing.RouteNode;
|
||||
import org.osmdroid.routing.RouteProvider;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.osmdroid.utils.PolylineEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* class to get a route between a start and a destination point, going through a
|
||||
* list of waypoints. It uses MapQuest open, public and free API, based on
|
||||
* OpenStreetMap data. <br>
|
||||
* See http://open.mapquestapi.com/guidance
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class MapQuestRouteProvider extends RouteProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(MapQuestRouteProvider.class);
|
||||
|
||||
static final String MAPQUEST_GUIDANCE_SERVICE = "http://open.mapquestapi.com/guidance/v0/route?";
|
||||
|
||||
/**
|
||||
* Build the URL to MapQuest service returning a route in XML format
|
||||
*
|
||||
* @param waypoints : array of waypoints, as [lat, lng], from start point to end
|
||||
* point.
|
||||
* @return ...
|
||||
*/
|
||||
protected String getUrl(List<GeoPoint> waypoints) {
|
||||
StringBuffer urlString = new StringBuffer(MAPQUEST_GUIDANCE_SERVICE);
|
||||
urlString.append("from=");
|
||||
GeoPoint p = waypoints.get(0);
|
||||
urlString.append(geoPointAsString(p));
|
||||
|
||||
for (int i = 1; i < waypoints.size(); i++) {
|
||||
p = waypoints.get(i);
|
||||
urlString.append("&to=" + geoPointAsString(p));
|
||||
}
|
||||
|
||||
urlString.append("&outFormat=xml");
|
||||
urlString.append("&shapeFormat=cmp"); // encoded polyline, much faster
|
||||
|
||||
urlString.append("&narrativeType=text"); // or "none"
|
||||
// Locale locale = Locale.getDefault();
|
||||
// urlString.append("&locale="+locale.getLanguage()+"_"+locale.getCountry());
|
||||
|
||||
urlString.append("&unit=k&fishbone=false");
|
||||
|
||||
// urlString.append("&generalizeAfter=500" /*+&generalize=2"*/);
|
||||
// 500 points max, 2 meters tolerance
|
||||
|
||||
// Warning: MapQuest Open API doc is sometimes WRONG:
|
||||
// - use unit, not units
|
||||
// - use fishbone, not enableFishbone
|
||||
// - locale (fr_FR, en_US) is supported but not documented.
|
||||
// - generalize and generalizeAfter are not properly implemented
|
||||
urlString.append(mOptions);
|
||||
return urlString.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param waypoints : list of GeoPoints. Must have at least 2 entries, start and
|
||||
* end points.
|
||||
* @return the route
|
||||
*/
|
||||
@Override
|
||||
public Route getRoute(List<GeoPoint> waypoints) {
|
||||
String url = getUrl(waypoints);
|
||||
log.debug("MapQuestRouteManager.getRoute:" + url);
|
||||
Route route = null;
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(url);
|
||||
InputStream stream = connection.getStream();
|
||||
if (stream != null)
|
||||
route = getRouteXML(stream, waypoints);
|
||||
if (route == null || route.routeHigh.size() == 0) {
|
||||
// Create default route:
|
||||
route = new Route(waypoints);
|
||||
}
|
||||
connection.close();
|
||||
log.debug("MapQuestRouteManager.getRoute - finished");
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML implementation
|
||||
*
|
||||
* @param is : input stream to parse
|
||||
* @param waypoints ...
|
||||
* @return the route ...
|
||||
*/
|
||||
protected Route getRouteXML(InputStream is, List<GeoPoint> waypoints) {
|
||||
XMLHandler handler = new XMLHandler();
|
||||
try {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(is, handler);
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Route route = handler.mRoute;
|
||||
if (route != null && route.routeHigh.size() > 0) {
|
||||
route.nodes = finalizeNodes(route.nodes, handler.mLinks, route.routeHigh);
|
||||
route.buildLegs(waypoints);
|
||||
route.status = Route.STATUS_OK;
|
||||
}
|
||||
return route;
|
||||
}
|
||||
|
||||
protected List<RouteNode> finalizeNodes(List<RouteNode> mNodes,
|
||||
List<RouteLink> mLinks, List<GeoPoint> polyline) {
|
||||
int n = mNodes.size();
|
||||
if (n == 0)
|
||||
return mNodes;
|
||||
ArrayList<RouteNode> newNodes = new ArrayList<RouteNode>(n);
|
||||
RouteNode lastNode = null;
|
||||
for (int i = 1; i < n - 1; i++) { // 1, n-1 => first and last MapQuest
|
||||
// nodes are irrelevant.
|
||||
RouteNode node = mNodes.get(i);
|
||||
RouteLink link = mLinks.get(node.nextRouteLink);
|
||||
if (lastNode != null && (node.instructions == null || node.maneuverType == 0)) {
|
||||
// this node is irrelevant, don't keep it,
|
||||
// but update values of last node:
|
||||
lastNode.length += link.mLength;
|
||||
lastNode.duration += (node.duration + link.mDuration);
|
||||
} else {
|
||||
node.length = link.mLength;
|
||||
node.duration += link.mDuration;
|
||||
int locationIndex = link.mShapeIndex;
|
||||
node.location = polyline.get(locationIndex);
|
||||
newNodes.add(node);
|
||||
lastNode = node;
|
||||
}
|
||||
}
|
||||
// switch to the new array of nodes:
|
||||
return newNodes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route Link is a portion of route between 2 "nodes" or intersections
|
||||
*/
|
||||
class RouteLink {
|
||||
/**
|
||||
* in km/h
|
||||
*/
|
||||
public double mSpeed;
|
||||
/**
|
||||
* in km
|
||||
*/
|
||||
public double mLength;
|
||||
/**
|
||||
* in sec
|
||||
*/
|
||||
public double mDuration;
|
||||
/**
|
||||
* starting point of the link, as index in initial polyline
|
||||
*/
|
||||
public int mShapeIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* XMLHandler: class to handle XML generated by MapQuest "guidance" open API.
|
||||
*/
|
||||
class XMLHandler extends DefaultHandler {
|
||||
public Route mRoute;
|
||||
public ArrayList<RouteLink> mLinks;
|
||||
|
||||
boolean isBB;
|
||||
boolean isGuidanceNodeCollection;
|
||||
private String mString;
|
||||
double mLat, mLng;
|
||||
double mNorth, mWest, mSouth, mEast;
|
||||
RouteLink mLink;
|
||||
RouteNode mNode;
|
||||
|
||||
public XMLHandler() {
|
||||
isBB = isGuidanceNodeCollection = false;
|
||||
mRoute = new Route();
|
||||
mLinks = new ArrayList<RouteLink>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String name,
|
||||
Attributes attributes) {
|
||||
if (localName.equals("boundingBox"))
|
||||
isBB = true;
|
||||
else if (localName.equals("link"))
|
||||
mLink = new RouteLink();
|
||||
else if (localName.equals("node"))
|
||||
mNode = new RouteNode();
|
||||
else if (localName.equals("GuidanceNodeCollection"))
|
||||
isGuidanceNodeCollection = true;
|
||||
mString = new String();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
|
||||
*/
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
String chars = new String(ch, start, length);
|
||||
mString = mString.concat(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String name) {
|
||||
if (localName.equals("lat")) {
|
||||
mLat = Double.parseDouble(mString);
|
||||
} else if (localName.equals("lng")) {
|
||||
mLng = Double.parseDouble(mString);
|
||||
} else if (localName.equals("shapePoints")) {
|
||||
mRoute.routeHigh = PolylineEncoder.decode(mString, 10);
|
||||
// log.debug("High="+mRoute.mRouteHigh.size());
|
||||
} else if (localName.equals("generalizedShape")) {
|
||||
mRoute.setRouteLow(PolylineEncoder.decode(mString, 10));
|
||||
// log.debug("Low="+mRoute.mRouteLow.size());
|
||||
} else if (localName.equals("length")) {
|
||||
mLink.mLength = Double.parseDouble(mString);
|
||||
} else if (localName.equals("speed")) {
|
||||
mLink.mSpeed = Double.parseDouble(mString);
|
||||
} else if (localName.equals("shapeIndex")) {
|
||||
mLink.mShapeIndex = Integer.parseInt(mString);
|
||||
} else if (localName.equals("link")) {
|
||||
// End of a link: update route attributes:
|
||||
// GuidanceLinkCollection could in theory contain additional unused
|
||||
// links,
|
||||
// but normally not with fishbone set to false.
|
||||
mLink.mDuration = mLink.mLength / mLink.mSpeed * 3600.0;
|
||||
mLinks.add(mLink);
|
||||
mRoute.length += mLink.mLength;
|
||||
mRoute.duration += mLink.mDuration;
|
||||
mLink = null;
|
||||
} else if (localName.equals("turnCost")) {
|
||||
int turnCost = Integer.parseInt(mString);
|
||||
mNode.duration += turnCost;
|
||||
mRoute.duration += turnCost;
|
||||
} else if (localName.equals("maneuverType")) {
|
||||
mNode.maneuverType = Integer.parseInt(mString);
|
||||
} else if (localName.equals("info")) {
|
||||
if (isGuidanceNodeCollection) {
|
||||
if (mNode.instructions == null)
|
||||
// this is first "info" value for this node, keep it:
|
||||
mNode.instructions = mString;
|
||||
}
|
||||
} else if (localName.equals("linkId")) {
|
||||
if (isGuidanceNodeCollection)
|
||||
mNode.nextRouteLink = Integer.parseInt(mString);
|
||||
} else if (localName.equals("node")) {
|
||||
mRoute.nodes.add(mNode);
|
||||
mNode = null;
|
||||
} else if (localName.equals("GuidanceNodeCollection")) {
|
||||
isGuidanceNodeCollection = false;
|
||||
} else if (localName.equals("ul")) {
|
||||
if (isBB) {
|
||||
mNorth = mLat;
|
||||
mWest = mLng;
|
||||
}
|
||||
} else if (localName.equals("lr")) {
|
||||
if (isBB) {
|
||||
mSouth = mLat;
|
||||
mEast = mLng;
|
||||
}
|
||||
} else if (localName.equals("boundingBox")) {
|
||||
mRoute.boundingBox = new BoundingBox(mNorth, mEast, mSouth, mWest);
|
||||
isBB = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
294
vtm-app/src/org/osmdroid/routing/provider/OSRMRouteProvider.java
Normal file
294
vtm-app/src/org/osmdroid/routing/provider/OSRMRouteProvider.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package org.osmdroid.routing.provider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.routing.Route;
|
||||
import org.osmdroid.routing.RouteNode;
|
||||
import org.osmdroid.routing.RouteProvider;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.osmdroid.utils.PolylineEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* get a route between a start and a destination point. It uses OSRM, a free
|
||||
* open source routing service based on OpenSteetMap data. <br>
|
||||
* See https://github.com/DennisOSRM/Project-OSRM/wiki/Server-api<br>
|
||||
* It requests by default the OSRM demo site. Use setService() to request an
|
||||
* other (for instance your own) OSRM service. <br>
|
||||
* TODO: improve internationalization of instructions
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class OSRMRouteProvider extends RouteProvider {
|
||||
|
||||
final static Logger log = LoggerFactory.getLogger(OSRMRouteProvider.class);
|
||||
|
||||
// 1 for 6 digit precision, 10 for 5
|
||||
private final static int ENCODING_PRECISION = 1;
|
||||
|
||||
//static final String OSRM_SERVICE = "http://city.informatik.uni-bremen.de:5000/viaroute?";
|
||||
//static final String OSRM_SERVICE = "http://city.informatik.uni-bremen.de:5001/viaroute?";
|
||||
static final String OSRM_SERVICE = "http://router.project-osrm.org/viaroute?";
|
||||
|
||||
//Note that the result of OSRM is quite close to Cloudmade NavEngine format:
|
||||
//http://developers.cloudmade.com/wiki/navengine/JSON_format
|
||||
|
||||
protected String mServiceUrl;
|
||||
protected String mUserAgent;
|
||||
|
||||
/**
|
||||
* mapping from OSRM directions to MapQuest maneuver IDs:
|
||||
*/
|
||||
static final HashMap<String, Integer> MANEUVERS;
|
||||
|
||||
static {
|
||||
MANEUVERS = new HashMap<String, Integer>();
|
||||
MANEUVERS.put("0", Integer.valueOf(0)); //No instruction
|
||||
MANEUVERS.put("1", Integer.valueOf(1)); //Continue
|
||||
MANEUVERS.put("2", Integer.valueOf(6)); //Slight right
|
||||
MANEUVERS.put("3", Integer.valueOf(7)); //Right
|
||||
MANEUVERS.put("4", Integer.valueOf(8)); //Sharp right
|
||||
MANEUVERS.put("5", Integer.valueOf(12)); //U-turn
|
||||
MANEUVERS.put("6", Integer.valueOf(5)); //Sharp left
|
||||
MANEUVERS.put("7", Integer.valueOf(4)); //Left
|
||||
MANEUVERS.put("8", Integer.valueOf(3)); //Slight left
|
||||
MANEUVERS.put("9", Integer.valueOf(24)); //Arrived (at waypoint)
|
||||
//MANEUVERS.put("10", Integer.valueOf(0)); //"Head" => used by OSRM as the start node
|
||||
MANEUVERS.put("11-1", Integer.valueOf(27)); //Round-about, 1st exit
|
||||
MANEUVERS.put("11-2", Integer.valueOf(28)); //2nd exit, etc ...
|
||||
MANEUVERS.put("11-3", Integer.valueOf(29));
|
||||
MANEUVERS.put("11-4", Integer.valueOf(30));
|
||||
MANEUVERS.put("11-5", Integer.valueOf(31));
|
||||
MANEUVERS.put("11-6", Integer.valueOf(32));
|
||||
MANEUVERS.put("11-7", Integer.valueOf(33));
|
||||
MANEUVERS.put("11-8", Integer.valueOf(34)); //Round-about, 8th exit
|
||||
MANEUVERS.put("15", Integer.valueOf(24)); //Arrived
|
||||
}
|
||||
|
||||
//From: Project-OSRM-Web / WebContent / localization / OSRM.Locale.en.js
|
||||
// driving directions
|
||||
// %s: route name
|
||||
// %d: direction => removed
|
||||
// <*>: will only be printed when there actually is a route name
|
||||
static final HashMap<String, HashMap<String, String>> DIRECTIONS;
|
||||
|
||||
static {
|
||||
DIRECTIONS = new HashMap<String, HashMap<String, String>>();
|
||||
HashMap<String, String> directions;
|
||||
|
||||
directions = new HashMap<String, String>();
|
||||
DIRECTIONS.put("en", directions);
|
||||
directions.put("0", "Unknown instruction< on %s>");
|
||||
directions.put("1", "Continue< on %s>");
|
||||
directions.put("2", "Turn slight right< on %s>");
|
||||
directions.put("3", "Turn right< on %s>");
|
||||
directions.put("4", "Turn sharp right< on %s>");
|
||||
directions.put("5", "U-Turn< on %s>");
|
||||
directions.put("6", "Turn sharp left< on %s>");
|
||||
directions.put("7", "Turn left< on %s>");
|
||||
directions.put("8", "Turn slight left< on %s>");
|
||||
directions.put("9", "You have reached a waypoint of your trip");
|
||||
directions.put("10", "<Go on %s>");
|
||||
directions.put("11-1", "Enter roundabout and leave at first exit< on %s>");
|
||||
directions.put("11-2", "Enter roundabout and leave at second exit< on %s>");
|
||||
directions.put("11-3", "Enter roundabout and leave at third exit< on %s>");
|
||||
directions.put("11-4", "Enter roundabout and leave at fourth exit< on %s>");
|
||||
directions.put("11-5", "Enter roundabout and leave at fifth exit< on %s>");
|
||||
directions.put("11-6", "Enter roundabout and leave at sixth exit< on %s>");
|
||||
directions.put("11-7", "Enter roundabout and leave at seventh exit< on %s>");
|
||||
directions.put("11-8", "Enter roundabout and leave at eighth exit< on %s>");
|
||||
directions.put("11-9", "Enter roundabout and leave at nineth exit< on %s>");
|
||||
directions.put("15", "You have reached your destination");
|
||||
|
||||
directions = new HashMap<String, String>();
|
||||
DIRECTIONS.put("fr", directions);
|
||||
directions.put("0", "Instruction inconnue< sur %s>");
|
||||
directions.put("1", "Continuez< sur %s>");
|
||||
directions.put("2", "Tournez légèrement à droite< sur %s>");
|
||||
directions.put("3", "Tournez à droite< sur %s>");
|
||||
directions.put("4", "Tournez fortement à droite< sur %s>");
|
||||
directions.put("5", "Faites demi-tour< sur %s>");
|
||||
directions.put("6", "Tournez fortement à gauche< sur %s>");
|
||||
directions.put("7", "Tournez à gauche< sur %s>");
|
||||
directions.put("8", "Tournez légèrement à gauche< sur %s>");
|
||||
directions.put("9", "Vous êtes arrivé à une étape de votre voyage");
|
||||
directions.put("10", "<Prenez %s>");
|
||||
directions.put("11-1", "Au rond-point, prenez la première sortie< sur %s>");
|
||||
directions.put("11-2", "Au rond-point, prenez la deuxième sortie< sur %s>");
|
||||
directions.put("11-3", "Au rond-point, prenez la troisième sortie< sur %s>");
|
||||
directions.put("11-4", "Au rond-point, prenez la quatrième sortie< sur %s>");
|
||||
directions.put("11-5", "Au rond-point, prenez la cinquième sortie< sur %s>");
|
||||
directions.put("11-6", "Au rond-point, prenez la sixième sortie< sur %s>");
|
||||
directions.put("11-7", "Au rond-point, prenez la septième sortie< sur %s>");
|
||||
directions.put("11-8", "Au rond-point, prenez la huitième sortie< sur %s>");
|
||||
directions.put("11-9", "Au rond-point, prenez la neuvième sortie< sur %s>");
|
||||
directions.put("15", "Vous êtes arrivé");
|
||||
|
||||
directions = new HashMap<String, String>();
|
||||
DIRECTIONS.put("pl", directions);
|
||||
directions.put("0", "Nieznana instrukcja<w %s>");
|
||||
directions.put("1", "Kontynuuj jazdę<na %s>");
|
||||
directions.put("2", "Skręć lekko w prawo<w %s>");
|
||||
directions.put("3", "Skręć w prawo<w %s>");
|
||||
directions.put("4", "Skręć ostro w prawo<w %s>");
|
||||
directions.put("5", "Zawróć<na %s>");
|
||||
directions.put("6", "Skręć ostro w lewo<w %s>");
|
||||
directions.put("7", "Skręć w lewo<w %s>");
|
||||
directions.put("8", "Skręć lekko w lewo<w %s>");
|
||||
directions.put("9", "Dotarłeś do punktu pośredniego");
|
||||
directions.put("10", "<Jedź %s>");
|
||||
directions.put("11-1", "Wjedź na rondo i opuść je pierwszym zjazdem<w %s>");
|
||||
directions.put("11-2", "Wjedź na rondo i opuść je drugim zjazdem<w %s>");
|
||||
directions.put("11-3", "Wjedź na rondo i opuść je trzecim zjazdem<w %s>");
|
||||
directions.put("11-4", "Wjedź na rondo i opuść je czwartym zjazdem<w %s>");
|
||||
directions.put("11-5", "Wjedź na rondo i opuść je piątym zjazdem<w %s>");
|
||||
directions.put("11-6", "Wjedź na rondo i opuść je szóstym zjazdem<w %s>");
|
||||
directions.put("11-7", "Wjedź na rondo i opuść je siódmym zjazdem<w %s>");
|
||||
directions.put("11-8", "Wjedź na rondo i opuść je ósmym zjazdem<w %s>");
|
||||
directions.put("11-9", "Wjedź na rondo i opuść je dziewiątym zjazdem<w %s>");
|
||||
directions.put("15", "Dotarłeś do celu podróży");
|
||||
}
|
||||
|
||||
public OSRMRouteProvider() {
|
||||
super();
|
||||
mServiceUrl = OSRM_SERVICE;
|
||||
mUserAgent = BonusPackHelper.DEFAULT_USER_AGENT; //set user agent to the default one.
|
||||
}
|
||||
|
||||
/**
|
||||
* allows to request on an other site than OSRM demo site
|
||||
*
|
||||
* @param serviceUrl ...
|
||||
*/
|
||||
public void setService(String serviceUrl) {
|
||||
mServiceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* allows to send to OSRM service a user agent specific to the app, instead
|
||||
* of the default user agent of OSMBonusPack lib.
|
||||
*
|
||||
* @param userAgent ...
|
||||
*/
|
||||
public void setUserAgent(String userAgent) {
|
||||
mUserAgent = userAgent;
|
||||
}
|
||||
|
||||
protected String getUrl(List<GeoPoint> waypoints) {
|
||||
StringBuffer urlString = new StringBuffer(mServiceUrl);
|
||||
for (int i = 0; i < waypoints.size(); i++) {
|
||||
GeoPoint p = waypoints.get(i);
|
||||
urlString.append("&loc=" + geoPointAsString(p));
|
||||
}
|
||||
urlString.append(mOptions);
|
||||
return urlString.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getRoute(List<GeoPoint> waypoints) {
|
||||
String url = getUrl(waypoints);
|
||||
log.debug("OSRMRouteManager.getRoute:" + url);
|
||||
|
||||
//String jString = BonusPackHelper.requestStringFromUrl(url);
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.setUserAgent(mUserAgent);
|
||||
connection.doGet(url);
|
||||
String jString = connection.getContentAsString();
|
||||
connection.close();
|
||||
|
||||
if (jString == null) {
|
||||
log.error("OSRMRouteManager::getRoute: request failed.");
|
||||
return new Route(waypoints);
|
||||
}
|
||||
Locale l = Locale.getDefault();
|
||||
HashMap<String, String> directions = DIRECTIONS.get(l.getLanguage());
|
||||
if (directions == null)
|
||||
directions = DIRECTIONS.get("en");
|
||||
Route route = new Route();
|
||||
try {
|
||||
JSONObject jObject = new JSONObject(jString);
|
||||
String route_geometry = jObject.getString("route_geometry");
|
||||
route.routeHigh = PolylineEncoder.decode(route_geometry, ENCODING_PRECISION);
|
||||
JSONArray jInstructions = jObject.getJSONArray("route_instructions");
|
||||
int n = jInstructions.length();
|
||||
RouteNode lastNode = null;
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONArray jInstruction = jInstructions.getJSONArray(i);
|
||||
RouteNode node = new RouteNode();
|
||||
int positionIndex = jInstruction.getInt(3);
|
||||
node.location = route.routeHigh.get(positionIndex);
|
||||
node.length = jInstruction.getInt(2) / 1000.0;
|
||||
node.duration = jInstruction.getInt(4); //Segment duration in seconds.
|
||||
String direction = jInstruction.getString(0);
|
||||
String routeName = jInstruction.getString(1);
|
||||
if (lastNode != null && "1".equals(direction) && "".equals(routeName)) {
|
||||
//node "Continue" with no route name is useless, don't add it
|
||||
lastNode.length += node.length;
|
||||
lastNode.duration += node.duration;
|
||||
} else {
|
||||
node.maneuverType = getManeuverCode(direction);
|
||||
node.instructions = buildInstructions(direction, routeName, directions);
|
||||
//log.debug(direction+"=>"+node.mManeuverType+"; "+node.mInstructions);
|
||||
route.nodes.add(node);
|
||||
lastNode = node;
|
||||
}
|
||||
}
|
||||
JSONObject jSummary = jObject.getJSONObject("route_summary");
|
||||
route.length = jSummary.getInt("total_distance") / 1000.0;
|
||||
route.duration = jSummary.getInt("total_time");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return new Route(waypoints);
|
||||
}
|
||||
if (route.routeHigh.size() == 0) {
|
||||
//Create default route:
|
||||
route = new Route(waypoints);
|
||||
} else {
|
||||
route.buildLegs(waypoints);
|
||||
BoundingBox bb = BoundingBox.fromGeoPoints(route.routeHigh);
|
||||
//Correcting osmdroid bug #359:
|
||||
route.boundingBox = bb;
|
||||
// new BoundingBox(
|
||||
// bb.getLatSouthE6(), bb.getLonWestE6(), bb.getLatNorthE6(), bb.getLonEastE6());
|
||||
route.status = Route.STATUS_OK;
|
||||
}
|
||||
log.debug("OSRMRouteManager.getRoute - finished");
|
||||
return route;
|
||||
}
|
||||
|
||||
protected int getManeuverCode(String direction) {
|
||||
Integer code = MANEUVERS.get(direction);
|
||||
if (code != null)
|
||||
return code.intValue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected String buildInstructions(String direction, String routeName,
|
||||
HashMap<String, String> directions) {
|
||||
if (directions == null)
|
||||
return null;
|
||||
direction = directions.get(direction);
|
||||
if (direction == null)
|
||||
return null;
|
||||
String instructions = null;
|
||||
if (routeName.equals(""))
|
||||
//remove "<*>"
|
||||
instructions = direction.replaceFirst("<[^>]*>", "");
|
||||
else {
|
||||
direction = direction.replace('<', ' ');
|
||||
direction = direction.replace('>', ' ');
|
||||
instructions = String.format(direction, routeName);
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
}
|
||||
112
vtm-app/src/org/osmdroid/utils/BonusPackHelper.java
Normal file
112
vtm-app/src/org/osmdroid/utils/BonusPackHelper.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package org.osmdroid.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Useful functions and common constants.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class BonusPackHelper {
|
||||
|
||||
/**
|
||||
* Log tag.
|
||||
*/
|
||||
public static final String LOG_TAG = "BONUSPACK";
|
||||
|
||||
/**
|
||||
* User agent sent to services by default
|
||||
*/
|
||||
public static final String DEFAULT_USER_AGENT = "OsmBonusPack/1";
|
||||
|
||||
/**
|
||||
* @return true if the device is the emulator, false if actual device.
|
||||
*/
|
||||
public static boolean isEmulator() {
|
||||
//return Build.MANUFACTURER.equals("unknown");
|
||||
return ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param connection ...
|
||||
* @return the whole content of the http request, as a string
|
||||
*/
|
||||
private static String readStream(HttpConnection connection) {
|
||||
String result = connection.getContentAsString();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends an http request, and returns the whole content result in a String.
|
||||
*
|
||||
* @param url ...
|
||||
* @return the whole content, or null if any issue.
|
||||
*/
|
||||
public static String requestStringFromUrl(String url) {
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(url);
|
||||
String result = readStream(connection);
|
||||
connection.close();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a bitmap from a url.
|
||||
*
|
||||
* @param url ...
|
||||
* @return the bitmap, or null if any issue.
|
||||
*/
|
||||
public static Bitmap loadBitmap(String url) {
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
InputStream is = (InputStream) new URL(url).getContent();
|
||||
bitmap = BitmapFactory.decodeStream(new FlushedInputStream(is));
|
||||
//Alternative providing better handling on loading errors?
|
||||
/* Drawable d = Drawable.createFromStream(new
|
||||
* FlushedInputStream(is), null); if (is != null) is.close(); if (d
|
||||
* != null) bitmap = ((BitmapDrawable)d).getBitmap(); */
|
||||
} catch (FileNotFoundException e) {
|
||||
//log.debug("image not available: " + url);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround on Android issue see
|
||||
* http://stackoverflow.com/questions/4601352
|
||||
* /createfromstream-in-android-returning-null-for-certain-url
|
||||
*/
|
||||
static class FlushedInputStream extends FilterInputStream {
|
||||
public FlushedInputStream(InputStream inputStream) {
|
||||
super(inputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
long totalBytesSkipped = 0L;
|
||||
while (totalBytesSkipped < n) {
|
||||
long bytesSkipped = in.skip(n - totalBytesSkipped);
|
||||
if (bytesSkipped == 0L) {
|
||||
int byteValue = read();
|
||||
if (byteValue < 0)
|
||||
break; // we reached EOF
|
||||
|
||||
bytesSkipped = 1; // we read one byte
|
||||
}
|
||||
totalBytesSkipped += bytesSkipped;
|
||||
}
|
||||
return totalBytesSkipped;
|
||||
}
|
||||
}
|
||||
}
|
||||
141
vtm-app/src/org/osmdroid/utils/DouglasPeuckerReducer.java
Normal file
141
vtm-app/src/org/osmdroid/utils/DouglasPeuckerReducer.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package org.osmdroid.utils;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reduces the number of points in a shape using the Douglas-Peucker algorithm. <br>
|
||||
* From:
|
||||
* http://www.phpriot.com/articles/reducing-map-path-douglas-peucker-algorithm/4<br>
|
||||
* Ported from PHP to Java. "marked" array added to optimize.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class DouglasPeuckerReducer {
|
||||
|
||||
/**
|
||||
* Reduce the number of points in a shape using the Douglas-Peucker
|
||||
* algorithm
|
||||
*
|
||||
* @param shape The shape to reduce
|
||||
* @param tolerance The tolerance to decide whether or not to keep a point, in the
|
||||
* coordinate system of the points (micro-degrees here)
|
||||
* @return the reduced shape
|
||||
*/
|
||||
public static List<GeoPoint> reduceWithTolerance(List<GeoPoint> shape,
|
||||
double tolerance) {
|
||||
int n = shape.size();
|
||||
// if a shape has 2 or less points it cannot be reduced
|
||||
if (tolerance <= 0 || n < 3) {
|
||||
return shape;
|
||||
}
|
||||
|
||||
boolean[] marked = new boolean[n]; //vertex indexes to keep will be marked as "true"
|
||||
for (int i = 1; i < n - 1; i++)
|
||||
marked[i] = false;
|
||||
// automatically add the first and last point to the returned shape
|
||||
marked[0] = marked[n - 1] = true;
|
||||
|
||||
// the first and last points in the original shape are
|
||||
// used as the entry point to the algorithm.
|
||||
douglasPeuckerReduction(
|
||||
shape, // original shape
|
||||
marked, // reduced shape
|
||||
tolerance, // tolerance
|
||||
0, // index of first point
|
||||
n - 1 // index of last point
|
||||
);
|
||||
|
||||
// all done, return the reduced shape
|
||||
ArrayList<GeoPoint> newShape = new ArrayList<GeoPoint>(n); // the new shape to return
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (marked[i])
|
||||
newShape.add(shape.get(i));
|
||||
}
|
||||
return newShape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the points in shape between the specified first and last index.
|
||||
* Mark the points to keep in marked[]
|
||||
*
|
||||
* @param shape The original shape
|
||||
* @param marked The points to keep (marked as true)
|
||||
* @param tolerance The tolerance to determine if a point is kept
|
||||
* @param firstIdx The index in original shape's point of the starting point for
|
||||
* this line segment
|
||||
* @param lastIdx The index in original shape's point of the ending point for
|
||||
* this line segment
|
||||
*/
|
||||
private static void douglasPeuckerReduction(List<GeoPoint> shape, boolean[] marked,
|
||||
double tolerance, int firstIdx, int lastIdx) {
|
||||
if (lastIdx <= firstIdx + 1) {
|
||||
// overlapping indexes, just return
|
||||
return;
|
||||
}
|
||||
|
||||
// loop over the points between the first and last points
|
||||
// and find the point that is the farthest away
|
||||
|
||||
double maxDistance = 0.0;
|
||||
int indexFarthest = 0;
|
||||
|
||||
GeoPoint firstPoint = shape.get(firstIdx);
|
||||
GeoPoint lastPoint = shape.get(lastIdx);
|
||||
|
||||
for (int idx = firstIdx + 1; idx < lastIdx; idx++) {
|
||||
GeoPoint point = shape.get(idx);
|
||||
|
||||
double distance = orthogonalDistance(point, firstPoint, lastPoint);
|
||||
|
||||
// keep the point with the greatest distance
|
||||
if (distance > maxDistance) {
|
||||
maxDistance = distance;
|
||||
indexFarthest = idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDistance > tolerance) {
|
||||
//The farthest point is outside the tolerance: it is marked and the algorithm continues.
|
||||
marked[indexFarthest] = true;
|
||||
|
||||
// reduce the shape between the starting point to newly found point
|
||||
douglasPeuckerReduction(shape, marked, tolerance, firstIdx, indexFarthest);
|
||||
|
||||
// reduce the shape between the newly found point and the finishing point
|
||||
douglasPeuckerReduction(shape, marked, tolerance, indexFarthest, lastIdx);
|
||||
}
|
||||
//else: the farthest point is within the tolerance, the whole segment is discarded.
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the orthogonal distance from the line joining the lineStart and
|
||||
* lineEnd points to point
|
||||
*
|
||||
* @param point The point the distance is being calculated for
|
||||
* @param lineStart The point that starts the line
|
||||
* @param lineEnd The point that ends the line
|
||||
* @return The distance in points coordinate system
|
||||
*/
|
||||
public static double orthogonalDistance(GeoPoint point, GeoPoint lineStart, GeoPoint lineEnd) {
|
||||
double area = Math.abs(
|
||||
(
|
||||
1.0 * lineStart.latitudeE6 * lineEnd.longitudeE6
|
||||
+ 1.0 * lineEnd.latitudeE6 * point.longitudeE6
|
||||
+ 1.0 * point.latitudeE6 * lineStart.longitudeE6
|
||||
- 1.0 * lineEnd.latitudeE6 * lineStart.longitudeE6
|
||||
- 1.0 * point.latitudeE6 * lineEnd.longitudeE6
|
||||
- 1.0 * lineStart.latitudeE6 * point.longitudeE6
|
||||
) / 2.0
|
||||
);
|
||||
|
||||
double bottom = Math.hypot(
|
||||
lineStart.latitudeE6 - lineEnd.latitudeE6,
|
||||
lineStart.longitudeE6 - lineEnd.longitudeE6
|
||||
);
|
||||
|
||||
return (area / bottom * 2.0);
|
||||
}
|
||||
}
|
||||
119
vtm-app/src/org/osmdroid/utils/HttpConnection.java
Normal file
119
vtm-app/src/org/osmdroid/utils/HttpConnection.java
Normal file
@@ -0,0 +1,119 @@
|
||||
package org.osmdroid.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A "very very simple to use" class for performing http get and post requests.
|
||||
* So many ways to do that, and potential subtle issues.
|
||||
* If complexity should be added to handle even more issues, complexity should be put here and only here.
|
||||
* <p/>
|
||||
* Typical usage:
|
||||
* <pre>HttpConnection connection = new HttpConnection();
|
||||
* connection.doGet("http://www.google.com");
|
||||
* InputStream stream = connection.getStream();
|
||||
* if (stream != null) {
|
||||
* //use this stream, for buffer reading, or XML SAX parsing, or whatever...
|
||||
* }
|
||||
* connection.close();</pre>
|
||||
*/
|
||||
public class HttpConnection {
|
||||
private final static int TIMEOUT_CONNECTION = 3000; //ms
|
||||
private final static int TIMEOUT_SOCKET = 10000; //ms
|
||||
|
||||
private static OkHttpClient client;
|
||||
private InputStream stream;
|
||||
private String mUserAgent;
|
||||
private Response response;
|
||||
|
||||
private static OkHttpClient getOkHttpClient() {
|
||||
if (client == null) {
|
||||
client = new OkHttpClient();
|
||||
client.setConnectTimeout(TIMEOUT_CONNECTION, TimeUnit.MILLISECONDS);
|
||||
client.setReadTimeout(TIMEOUT_SOCKET, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
public HttpConnection() {
|
||||
/*
|
||||
client = new OkHttpClient();
|
||||
client.setConnectTimeout(TIMEOUT_CONNECTION, TimeUnit.MILLISECONDS);
|
||||
client.setReadTimeout(TIMEOUT_SOCKET, TimeUnit.MILLISECONDS);
|
||||
*/
|
||||
}
|
||||
|
||||
public void setUserAgent(String userAgent) {
|
||||
mUserAgent = userAgent;
|
||||
}
|
||||
|
||||
public void doGet(final String url) {
|
||||
try {
|
||||
Request.Builder request = new Request.Builder().url(url);
|
||||
if (mUserAgent != null)
|
||||
request.addHeader("User-Agent", mUserAgent);
|
||||
response = getOkHttpClient().newCall(request.build()).execute();
|
||||
Integer status = response.code();
|
||||
if (status != 200) {
|
||||
Log.e(BonusPackHelper.LOG_TAG, "Invalid response from server: " + status.toString());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the opened InputStream, or null if creation failed for any reason.
|
||||
*/
|
||||
public InputStream getStream() {
|
||||
try {
|
||||
if (response == null)
|
||||
return null;
|
||||
stream = response.body().byteStream();
|
||||
return stream;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the whole content as a String, or null if creation failed for any reason.
|
||||
*/
|
||||
public String getContentAsString() {
|
||||
try {
|
||||
if (response == null)
|
||||
return null;
|
||||
return response.body().string();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling close once is mandatory.
|
||||
*/
|
||||
public void close() {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
stream = null;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (client != null)
|
||||
client = null;
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
22
vtm-app/src/org/osmdroid/utils/MathConstants.java
Normal file
22
vtm-app/src/org/osmdroid/utils/MathConstants.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.osmdroid.utils;
|
||||
|
||||
public class MathConstants {
|
||||
|
||||
public static final double PI180E6 = (Math.PI / 180) / 1000000.0;
|
||||
public static final double PIx4 = Math.PI * 4;
|
||||
|
||||
}
|
||||
94
vtm-app/src/org/osmdroid/utils/PolylineEncoder.java
Normal file
94
vtm-app/src/org/osmdroid/utils/PolylineEncoder.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package org.osmdroid.utils;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Methods to encode and decode a polyline with Google polyline
|
||||
* encoding/decoding scheme. See
|
||||
* https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
*/
|
||||
public class PolylineEncoder {
|
||||
|
||||
private static StringBuffer encodeSignedNumber(int num) {
|
||||
int sgn_num = num << 1;
|
||||
if (num < 0) {
|
||||
sgn_num = ~(sgn_num);
|
||||
}
|
||||
return (encodeNumber(sgn_num));
|
||||
}
|
||||
|
||||
private static StringBuffer encodeNumber(int num) {
|
||||
StringBuffer encodeString = new StringBuffer();
|
||||
while (num >= 0x20) {
|
||||
int nextValue = (0x20 | (num & 0x1f)) + 63;
|
||||
encodeString.append((char) (nextValue));
|
||||
num >>= 5;
|
||||
}
|
||||
num += 63;
|
||||
encodeString.append((char) (num));
|
||||
return encodeString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a polyline with Google polyline encoding method
|
||||
*
|
||||
* @param polyline the polyline
|
||||
* @param precision 1 for a 6 digits encoding, 10 for a 5 digits encoding.
|
||||
* @return the encoded polyline, as a String
|
||||
*/
|
||||
public static String encode(ArrayList<GeoPoint> polyline, int precision) {
|
||||
StringBuffer encodedPoints = new StringBuffer();
|
||||
int prev_lat = 0, prev_lng = 0;
|
||||
for (GeoPoint trackpoint : polyline) {
|
||||
int lat = trackpoint.latitudeE6 / precision;
|
||||
int lng = trackpoint.longitudeE6 / precision;
|
||||
encodedPoints.append(encodeSignedNumber(lat - prev_lat));
|
||||
encodedPoints.append(encodeSignedNumber(lng - prev_lng));
|
||||
prev_lat = lat;
|
||||
prev_lng = lng;
|
||||
}
|
||||
return encodedPoints.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a "Google-encoded" polyline
|
||||
*
|
||||
* @param encodedString ...
|
||||
* @param precision 1 for a 6 digits encoding, 10 for a 5 digits encoding.
|
||||
* @return the polyline.
|
||||
*/
|
||||
public static ArrayList<GeoPoint> decode(String encodedString, int precision) {
|
||||
ArrayList<GeoPoint> polyline = new ArrayList<GeoPoint>();
|
||||
int index = 0;
|
||||
int len = encodedString.length();
|
||||
int lat = 0, lng = 0;
|
||||
|
||||
while (index < len) {
|
||||
int b, shift = 0, result = 0;
|
||||
do {
|
||||
b = encodedString.charAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||
lat += dlat;
|
||||
|
||||
shift = 0;
|
||||
result = 0;
|
||||
do {
|
||||
b = encodedString.charAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||
lng += dlng;
|
||||
|
||||
GeoPoint p = new GeoPoint(lat * precision, lng * precision);
|
||||
polyline.add(p);
|
||||
}
|
||||
|
||||
return polyline;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user