diff --git a/DataExtractionOSM/src/com/osmand/ToDoConstants.java b/DataExtractionOSM/src/com/osmand/ToDoConstants.java index e4bc947d48..1f3be97ec9 100644 --- a/DataExtractionOSM/src/com/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/com/osmand/ToDoConstants.java @@ -25,8 +25,8 @@ public class ToDoConstants { // 61. Provide route information for YOURS (calclate turns/angle/expected time). // Fix some missing turns in CloudMade (for secondary roads wo name). Add them (if dist to prev/next turn > 150m) [dacha] // 33. Build transport locations. Create transport index (transport-stops) (investigate) - // DONE: Load transport routes in swing. - // IDEA TO HAVE : + // DONE : Load transport routes in swing, init + // TODO : add progress calculating (fix performance), fix area radius // 66. Transport routing (show next stop, total distance, show stop get out). diff --git a/DataExtractionOSM/src/com/osmand/osm/MapUtils.java b/DataExtractionOSM/src/com/osmand/osm/MapUtils.java index 2669a31e5d..8fdc8d85ef 100644 --- a/DataExtractionOSM/src/com/osmand/osm/MapUtils.java +++ b/DataExtractionOSM/src/com/osmand/osm/MapUtils.java @@ -213,7 +213,7 @@ public class MapUtils { } else if(meters >= 10000){ return MessageFormat.format("{0,number,#.#} "+Messages.getMessage(Messages.KEY_KM), ((float) meters) / 1000); //$NON-NLS-1$ } else if(meters > 1500){ - return MessageFormat.format("{0,number,#.##}"+Messages.getMessage(Messages.KEY_KM), ((float) meters) / 1000); //$NON-NLS-1$ + return MessageFormat.format("{0,number,#.##} "+Messages.getMessage(Messages.KEY_KM), ((float) meters) / 1000); //$NON-NLS-1$ } else { return meters + " "+Messages.getMessage(Messages.KEY_M); //$NON-NLS-1$ } diff --git a/OsmAnd/AndroidManifest.xml b/OsmAnd/AndroidManifest.xml index 599366af11..400a7f82d7 100644 --- a/OsmAnd/AndroidManifest.xml +++ b/OsmAnd/AndroidManifest.xml @@ -22,6 +22,7 @@ + @@ -40,4 +41,4 @@ - \ No newline at end of file + diff --git a/OsmAnd/res/layout/search_transport.xml b/OsmAnd/res/layout/search_transport.xml new file mode 100644 index 0000000000..5fdb0be309 --- /dev/null +++ b/OsmAnd/res/layout/search_transport.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/search_transport_list_item.xml b/OsmAnd/res/layout/search_transport_list_item.xml new file mode 100644 index 0000000000..6f35950bc9 --- /dev/null +++ b/OsmAnd/res/layout/search_transport_list_item.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/searchpoi_list.xml b/OsmAnd/res/layout/searchpoi_list.xml index 217026077f..fa5fad2aee 100644 --- a/OsmAnd/res/layout/searchpoi_list.xml +++ b/OsmAnd/res/layout/searchpoi_list.xml @@ -9,8 +9,8 @@ + android:textSize="22px" android:maxWidth="75dp" android:minWidth="75dp"/> + android:layout_height="wrap_content" android:textSize="22px" /> \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index c9f632f366..69d7fb5a46 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,5 +1,7 @@ + Transport + Show public transport stops on map Show transport stops Navigation application OsmAnd diff --git a/OsmAnd/src/com/osmand/ResourceManager.java b/OsmAnd/src/com/osmand/ResourceManager.java index 0171a29b4a..429d4d33c3 100644 --- a/OsmAnd/src/com/osmand/ResourceManager.java +++ b/OsmAnd/src/com/osmand/ResourceManager.java @@ -382,24 +382,7 @@ public class ResourceManager { } return repos; } - public List searchTransportStops(double latitude, double longitude, int zoom, int limit) { - double tileNumberX = MapUtils.getTileNumberX(zoom, longitude); - double tileNumberY = MapUtils.getTileNumberY(zoom, latitude); - double topLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY - 0.5); - double bottomLatitude = MapUtils.getLatitudeFromTile(zoom, tileNumberY + 0.5); - double leftLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX - 0.5); - double rightLongitude = MapUtils.getLongitudeFromTile(zoom, tileNumberX + 0.5); - List stops = new ArrayList(); - for (TransportIndexRepository index : transportRepositories.values()) { - if (index.checkContains(topLatitude, leftLongitude, bottomLatitude, rightLongitude)) { - if (!index.checkCachedObjects(topLatitude, leftLongitude, bottomLatitude, rightLongitude, zoom, stops)) { - index.searchTransportStops(topLatitude, leftLongitude, bottomLatitude, rightLongitude, limit, stops); - } - } - } - - return stops; - } + public void searchTransportAsync(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, List toFill){ for(TransportIndexRepository index : transportRepositories.values()){ diff --git a/OsmAnd/src/com/osmand/TransportIndexRepository.java b/OsmAnd/src/com/osmand/TransportIndexRepository.java index 7f0239f4f7..3483641ab8 100644 --- a/OsmAnd/src/com/osmand/TransportIndexRepository.java +++ b/OsmAnd/src/com/osmand/TransportIndexRepository.java @@ -3,17 +3,24 @@ package com.osmand; import java.io.File; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import android.database.Cursor; +import com.osmand.data.TransportRoute; import com.osmand.data.TransportStop; import com.osmand.data.index.IndexConstants; import com.osmand.data.index.IndexConstants.IndexTransportRoute; import com.osmand.data.index.IndexConstants.IndexTransportRouteStop; import com.osmand.data.index.IndexConstants.IndexTransportStop; +import com.osmand.osm.LatLon; +import com.osmand.osm.MapUtils; public class TransportIndexRepository extends BaseLocationIndexRepository { private static final Log log = LogUtil.getLog(TransportIndexRepository.class); @@ -74,7 +81,7 @@ public class TransportIndexRepository extends BaseLocationIndexRepository0){ + sql.append(", "); //$NON-NLS-1$ + } + sql.append(IndexTransportRoute.getTable()).append(".").append(cols[i]); //$NON-NLS-1$ + } + cols = IndexConstants.generateColumnNames(IndexTransportStop.values()); + for(int i=0; i registeredRoutes = new LinkedHashMap(); + if (query.moveToFirst()) { + do { + TransportRoute route = new TransportRoute(); + route.setId(query.getLong(IndexTransportRoute.ID.ordinal())); + route.setDistance(query.getInt(IndexTransportRoute.DIST.ordinal())); + route.setName(query.getString(IndexTransportRoute.NAME.ordinal())); + route.setEnName(query.getString(IndexTransportRoute.NAME_EN.ordinal())); + route.setRef(query.getString(IndexTransportRoute.REF.ordinal())); + route.setOperator(query.getString(IndexTransportRoute.OPERATOR.ordinal())); + route.setType(query.getString(IndexTransportRoute.TYPE.ordinal())); + TransportStop s = new TransportStop(); + s.setId(query.getLong(shift + IndexTransportStop.ID.ordinal())); + s.setName(query.getString(shift + IndexTransportStop.NAME.ordinal())); + s.setEnName(query.getString(shift + IndexTransportStop.NAME_EN.ordinal())); + s.setLocation(query.getDouble(shift + IndexTransportStop.LATITUDE.ordinal()), + query.getDouble(shift + IndexTransportStop.LONGITUDE.ordinal())); + boolean direction = query.getInt(shift2 + shift) > 0; + long idToPut = route.getId() << 1 + (direction ? 1 : 0); + if(registeredRoutes.containsKey(idToPut)){ + TransportStop st = registeredRoutes.get(idToPut).getStart(); + if(MapUtils.getDistance(loc, st.getLocation()) < MapUtils.getDistance(loc, s.getLocation())){ + continue; + } + } + RouteInfoLocation r = new RouteInfoLocation(); + r.setRoute(route); + r.setStart(s); + r.setDirection(direction); + registeredRoutes.put(idToPut, r); + + + } while (query.moveToNext()); + } + query.close(); + + if (log.isDebugEnabled()) { + log.debug(String.format("Search for routes done in %s ms found %s.", //$NON-NLS-1$ + System.currentTimeMillis() - now, registeredRoutes.size())); + } + + ArrayList list = new ArrayList(registeredRoutes.values()); + preloadRouteStopsAndCalculateDistance(loc, locationToGo, list); + return list; + + } + + private static String cacheSQLRouteStops = null; + protected void preloadRouteStopsAndCalculateDistance(final LatLon loc, LatLon locationToGo, List listRoutes){ + long now = System.currentTimeMillis(); + // TODO do 1 request + if(cacheSQLRouteStops == null){ + StringBuilder sql = new StringBuilder(200); + sql.append("SELECT "); //$NON-NLS-1$ + String[] cols = IndexConstants.generateColumnNames(IndexTransportStop.values()); + for(int i=0; i0){ + sql.append(", "); //$NON-NLS-1$ + } + sql.append(IndexTransportStop.getTable()).append(".").append(cols[i]); //$NON-NLS-1$ + } + + sql.append(" FROM ").append(IndexTransportRouteStop.getTable()); //$NON-NLS-1$ + // join with stops table + sql.append(" JOIN ").append(IndexTransportStop.getTable()); //$NON-NLS-1$ + sql.append(" ON ").append(IndexTransportStop.getTable()).append(".").append(IndexTransportStop.ID); //$NON-NLS-1$ //$NON-NLS-2$ + sql.append(" = ").append(IndexTransportRouteStop.getTable()).append(".").append(IndexTransportRouteStop.STOP); //$NON-NLS-1$ //$NON-NLS-2$ + + sql.append(" WHERE ").append("? = "). //$NON-NLS-1$//$NON-NLS-2$ + append(IndexTransportRouteStop.getTable()).append(".").append(IndexTransportRouteStop.ROUTE); //$NON-NLS-1$ + sql.append(" AND ?=").append(IndexTransportRouteStop.getTable()).append(".").append(IndexTransportRouteStop.DIRECTION); //$NON-NLS-1$ //$NON-NLS-2$ + cacheSQLRouteStops = sql.toString(); + } + + for(RouteInfoLocation i : listRoutes){ + int dist = 0; + if(locationToGo != null){ + dist = (int) MapUtils.getDistance(locationToGo, i.getStart().getLocation()); + } + + Cursor query = db.rawQuery(cacheSQLRouteStops, + new String[] {i.getRoute().getId()+"", i.getDirection()?"1":"0"}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (query.moveToFirst()) { + // load only part of the route + boolean found = false; + do { + TransportStop st = null; + if(found){ + st = new TransportStop(); + st.setId(query.getLong(IndexTransportStop.ID.ordinal())); + st.setLocation(query.getDouble(IndexTransportStop.LATITUDE.ordinal()), + query.getDouble(IndexTransportStop.LONGITUDE.ordinal())); + st.setName(query.getString(IndexTransportStop.NAME.ordinal() )); + st.setEnName(query.getString(IndexTransportStop.NAME_EN.ordinal())); + } else if(query.getLong(IndexTransportStop.ID.ordinal())== i.getStart().getId()){ + st = i.getStart(); + found = true; + } + + if(found){ + if(locationToGo != null){ + double d = MapUtils.getDistance(locationToGo,st.getLocation()); + if(d < dist){ + dist = (int) d; + } + } + if(i.direction){ + i.getRoute().getForwardStops().add(st); + } else { + i.getRoute().getBackwardStops().add(st); + } + } + } while (query.moveToNext()); + } + query.close(); + + if(locationToGo != null){ + i.setDistToLocation(dist); + } + } + + if (log.isDebugEnabled()) { + log.debug(String.format("Loading routes done in %s ms for %s routes.", //$NON-NLS-1$ + System.currentTimeMillis() - now, listRoutes.size())); + } + + if(locationToGo != null){ + Collections.sort(listRoutes, new Comparator(){ + @Override + public int compare(RouteInfoLocation object1, RouteInfoLocation object2) { + return object1.getDistToLocation() - object2.getDistToLocation(); + } + + }); + } else { + Collections.sort(listRoutes, new Comparator(){ + @Override + public int compare(RouteInfoLocation object1, RouteInfoLocation object2) { + return Double.compare(MapUtils.getDistance(loc, object1.getStart().getLocation()), + MapUtils.getDistance(loc, object2.getStart().getLocation())); + } + + }); + } + + } + + public static class RouteInfoLocation { + private TransportStop start; + private TransportRoute route; + private int distToLocation; + private boolean direction; + + public TransportStop getStart() { + return start; + } + + public TransportRoute getRoute() { + return route; + } + + public boolean getDirection(){ + return direction; + } + + public void setDirection(boolean direction) { + this.direction = direction; + } + + public int getDistToLocation() { + return distToLocation; + } + + public void setStart(TransportStop start) { + this.start = start; + } + + public void setRoute(TransportRoute route) { + this.route = route; + } + + public void setDistToLocation(int distToLocation) { + this.distToLocation = distToLocation; + } + } } diff --git a/OsmAnd/src/com/osmand/activities/DownloadIndexActivity.java b/OsmAnd/src/com/osmand/activities/DownloadIndexActivity.java index 30fd6f3ce7..919d19268e 100644 --- a/OsmAnd/src/com/osmand/activities/DownloadIndexActivity.java +++ b/OsmAnd/src/com/osmand/activities/DownloadIndexActivity.java @@ -212,7 +212,7 @@ public class DownloadIndexActivity extends ListActivity { } else if(e.getKey().endsWith(IndexConstants.ADDRESS_INDEX_EXT)){ s = getString(R.string.address); } else if(e.getKey().endsWith(IndexConstants.TRANSPORT_INDEX_EXT)){ - s = "Transport"; + s = getString(R.string.transport); } item.setText(s + "\n " + e.getKey().substring(0, l)); //$NON-NLS-1$ diff --git a/OsmAnd/src/com/osmand/activities/search/SearchActivity.java b/OsmAnd/src/com/osmand/activities/search/SearchActivity.java index b37679be6c..57a76191de 100644 --- a/OsmAnd/src/com/osmand/activities/search/SearchActivity.java +++ b/OsmAnd/src/com/osmand/activities/search/SearchActivity.java @@ -30,6 +30,7 @@ public class SearchActivity extends TabActivity { addressSpec = host.newTabSpec("Search_Address").setIndicator(getString(R.string.address)).setContent(new Intent(this, SearchAddressActivity.class));//$NON-NLS-1$ host.addTab(addressSpec); host.addTab(host.newTabSpec("Search_Location").setIndicator(getString(R.string.search_tabs_location)).setContent(new Intent(this, NavigatePointActivity.class))); //$NON-NLS-1$ + host.addTab(host.newTabSpec("Search_Transport").setIndicator(getString(R.string.transport)).setContent(new Intent(this, SearchTransportActivity.class))); //$NON-NLS-1$ host.addTab(host.newTabSpec("Search_History").setIndicator(getString(R.string.history)).setContent(new Intent(this, SearchHistoryActivity.class))); //$NON-NLS-1$ } diff --git a/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java b/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java new file mode 100644 index 0000000000..abbd989d86 --- /dev/null +++ b/OsmAnd/src/com/osmand/activities/search/SearchTransportActivity.java @@ -0,0 +1,187 @@ +/** + * + */ +package com.osmand.activities.search; + +import java.util.ArrayList; +import java.util.List; + +import android.app.AlertDialog; +import android.app.ListActivity; +import android.app.AlertDialog.Builder; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import com.osmand.Messages; +import com.osmand.OsmandSettings; +import com.osmand.R; +import com.osmand.ResourceManager; +import com.osmand.TransportIndexRepository; +import com.osmand.TransportIndexRepository.RouteInfoLocation; +import com.osmand.data.TransportRoute; +import com.osmand.data.TransportStop; +import com.osmand.osm.LatLon; +import com.osmand.osm.MapUtils; + +/** + * @author Maxim Frolov + * + */ +public class SearchTransportActivity extends ListActivity { + + + + private Button searchTransportLevel; + private TransportStopAdapter stopsAdapter; + private LatLon lastKnownMapLocation; + private LatLon locationToGo; + private TextView searchArea; + private TransportIndexRepository repo; + + private final static int finalZoom = 13; + private final static int initialZoom = 17; + private int zoom = initialZoom; + + + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.search_transport); + searchTransportLevel = (Button) findViewById(R.id.SearchPOILevelButton); + searchArea = (TextView) findViewById(R.id.SearchAreaText); + searchTransportLevel.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if(isSearchFurtherAvailable()){ + zoom --; + searchFurther(); + } + } + }); + + stopsAdapter = new TransportStopAdapter(new ArrayList()); + setListAdapter(stopsAdapter); + + } + + public String getSearchArea(){ + // TODO + if(zoom <= 14){ + int d = (int) (1 * (1 << (14 - zoom))); + return " < " + d + " " + Messages.getMessage(Messages.KEY_KM); //$NON-NLS-1$//$NON-NLS-2$ + } else { + return " < 500 " + Messages.getMessage(Messages.KEY_M); //$NON-NLS-1$ + } + } + public boolean isSearchFurtherAvailable() { + return zoom >= finalZoom; + } + + public void searchFurther(){ + if (lastKnownMapLocation != null) { + List rs = ResourceManager.getResourceManager().searchTransportRepositories(lastKnownMapLocation.getLatitude(), + lastKnownMapLocation.getLongitude()); + if(!rs.isEmpty()){ + repo = rs.get(0); + searchTransportLevel.setEnabled(isSearchFurtherAvailable()); + List res = repo.searchTransportRouteStops(lastKnownMapLocation.getLatitude(), lastKnownMapLocation.getLongitude(), + locationToGo, zoom); + // TODO add progress + stopsAdapter.setNewModel(res); + } else { + repo = null; + stopsAdapter.clear(); + } + } else { + stopsAdapter.clear(); + } + searchTransportLevel.setEnabled(isSearchFurtherAvailable()); + searchArea.setText(getSearchArea()); + } + + @Override + protected void onResume() { + super.onResume(); + lastKnownMapLocation = OsmandSettings.getLastKnownMapLocation(this); + locationToGo = OsmandSettings.getPointToNavigate(this); + searchFurther(); + } + + + + public void onListItemClick(ListView parent, View v, int position, long id) { + RouteInfoLocation item = ((TransportStopAdapter)getListAdapter()).getItem(position); + Builder builder = new AlertDialog.Builder(this); + List items = new ArrayList(); + List stops = item.getDirection() ? item.getRoute().getForwardStops() : item.getRoute().getBackwardStops(); + for(TransportStop st : stops){ + String n = st.getName(OsmandSettings.usingEnglishNames(this)); + if(locationToGo != null){ + n += " - " + MapUtils.getFormattedDistance((int) MapUtils.getDistance(locationToGo, st.getLocation())); //$NON-NLS-1$ + } + items.add(n); + } + builder.setItems(items.toArray(new String[items.size()]), null); + builder.show(); + } + + + class TransportStopAdapter extends ArrayAdapter { + TransportStopAdapter(List list) { + super(SearchTransportActivity.this, R.layout.search_transport_list_item, list); + this.setNotifyOnChange(false); + } + + public void setNewModel(List stopsList) { + setNotifyOnChange(false); + ((TransportStopAdapter) getListAdapter()).clear(); + for (RouteInfoLocation obj : stopsList) { + this.add(obj); + } + this.notifyDataSetChanged(); + setNotifyOnChange(true); + } + + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + if (row == null) { + LayoutInflater inflater = getLayoutInflater(); + row = inflater.inflate(R.layout.search_transport_list_item, parent, false); + } + TextView label = (TextView) row.findViewById(R.id.label); + TextView distanceLabel = (TextView) row.findViewById(R.id.distance); + ImageView icon = (ImageView) row.findViewById(R.id.search_icon); + RouteInfoLocation stop = getItem(position); + + TransportRoute route = stop.getRoute(); + StringBuilder labelW = new StringBuilder(150); + labelW.append(route.getType()).append(" ").append(route.getRef()); //$NON-NLS-1$ + if (locationToGo != null) { + labelW.append(" - ").append(MapUtils.getFormattedDistance(stop.getDistToLocation())); //$NON-NLS-1$ + } + labelW.append("\n").append(route.getName(OsmandSettings.usingEnglishNames(SearchTransportActivity.this))); //$NON-NLS-1$ + label.setText(labelW.toString()); + // TODO icons + if (stop.getDistToLocation() < 400) { + icon.setImageResource(R.drawable.poi); + } else { + icon.setImageResource(R.drawable.closed_poi); + } + int dist = (int) (MapUtils.getDistance(stop.getStart().getLocation(), lastKnownMapLocation)); + distanceLabel.setText(" " + MapUtils.getFormattedDistance(dist)); //$NON-NLS-1$ + + return (row); + } + } + +} diff --git a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java index db9028fbed..803eb20247 100644 --- a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java +++ b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java @@ -3,6 +3,7 @@ package com.osmand.views; import java.util.ArrayList; import java.util.List; +import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -14,6 +15,7 @@ import android.widget.Toast; import com.osmand.OsmandSettings; import com.osmand.ResourceManager; import com.osmand.TransportIndexRepository; +import com.osmand.activities.search.SearchTransportActivity; import com.osmand.data.TransportStop; import com.osmand.osm.MapUtils; @@ -144,8 +146,8 @@ public class TransportStopsLayer implements OsmandMapLayer { @Override public boolean onLongPressEvent(PointF point) { - // TODO open search transport - return false; + view.getContext().startActivity(new Intent(view.getContext(), SearchTransportActivity.class)); + return true; } }