add transport index

git-svn-id: https://osmand.googlecode.com/svn/trunk@269 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-07-01 12:41:57 +00:00
parent 508acd5fe3
commit 29703f0d70
9 changed files with 395 additions and 26 deletions

View file

@ -21,27 +21,28 @@ public class ToDoConstants {
// 50. Invent opening hours editor in order to edit POI hours better on device
// GOT by Olga
// TODO sort hamlets by distance
// 60. Audio guidance for routing
// 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 :
// 43. Enable poi filter by name
// 58. Upload/Download zip-index from site & unzip them on phone
// 45. Get clear <Use internet> settings. Move that setting on top settings screen.
// That setting should rule all activities that use internet. It should ask whenever internet is used
// (would you like to use internet for that operation - if using internet is not checked).
// Internet using now for : edit POI osm, show osm bugs layer, download tiles.
// 64. Traffic information (?)
// 65. Intermediate points (?)
// 40. Support simple vector road rendering (require new index file) (?)
// 63. Support simple offline routing(require new index file) (?)
// BUGS Android
// FIXME !!!! Check agains ID is not unique ! (for relation/node/way - it could be the same)
// FIXME !!!! Check agains ID is not unique ! (for relation/node/way - it could be the same) - checked for data extraction & index creator
// TODO swing
// 9. Fix issues with big files (such as netherlands) - save memory (!) - very slow due to transport index !

View file

@ -103,5 +103,10 @@ public abstract class MapObject implements Comparable<MapObject> {
public void doDataPreparation() {
}
@Override
public String toString() {
return getClass().getSimpleName() + " " + name +"("+id+")";
}
}

View file

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.osmand.osm.MapUtils;
import com.osmand.osm.Relation;
import com.osmand.osm.Way;
@ -12,6 +13,8 @@ public class TransportRoute extends MapObject {
private List<TransportStop> forwardStops = new ArrayList<TransportStop>();
private List<TransportStop> backwardStops = new ArrayList<TransportStop>();
private String ref;
private String operator;
private String type;
public TransportRoute(Relation r, String ref){
super(r);
@ -48,5 +51,32 @@ public class TransportRoute extends MapObject {
this.ref = ref;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getAvgBothDistance(){
int d = 0;
for(int i=1; i< backwardStops.size(); i++){
d += MapUtils.getDistance(backwardStops.get(i-1).getLocation(), backwardStops.get(i).getLocation());
}
for(int i=1; i< forwardStops.size(); i++){
d += MapUtils.getDistance(forwardStops.get(i-1).getLocation(), forwardStops.get(i).getLocation());
}
return d;
}
}

View file

@ -8,7 +8,10 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
@ -19,12 +22,17 @@ import com.osmand.data.Building;
import com.osmand.data.City;
import com.osmand.data.Region;
import com.osmand.data.Street;
import com.osmand.data.TransportRoute;
import com.osmand.data.TransportStop;
import com.osmand.data.City.CityType;
import com.osmand.data.index.IndexConstants.IndexBuildingTable;
import com.osmand.data.index.IndexConstants.IndexCityTable;
import com.osmand.data.index.IndexConstants.IndexPoiTable;
import com.osmand.data.index.IndexConstants.IndexStreetNodeTable;
import com.osmand.data.index.IndexConstants.IndexStreetTable;
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.Node;
import com.osmand.osm.Way;
@ -239,6 +247,107 @@ public class DataIndexWriter {
return this;
}
public DataIndexWriter writeTransport() throws IOException, SQLException{
return writeTransport(IndexConstants.TRANSPORT_INDEX_DIR+region.getName()+IndexConstants.TRANSPORT_INDEX_EXT, null);
}
public DataIndexWriter writeTransport(String fileName, Long date) throws IOException, SQLException{
File file = checkFile(fileName);
long now = System.currentTimeMillis();
try {
Class.forName("org.sqlite.JDBC"); //$NON-NLS-1$
} catch (ClassNotFoundException e) {
log.error("Illegal configuration", e); //$NON-NLS-1$
throw new IllegalStateException(e);
}
Connection conn = DriverManager.getConnection("jdbc:sqlite:"+file.getAbsolutePath()); //$NON-NLS-1$
try {
Statement stat = conn.createStatement();
stat.executeUpdate(IndexConstants.generateCreateSQL(IndexTransportRoute.values()));
stat.executeUpdate(IndexConstants.generateCreateIndexSQL(IndexTransportRoute.values()));
stat.executeUpdate(IndexConstants.generateCreateSQL(IndexTransportRouteStop.values()));
stat.executeUpdate(IndexConstants.generateCreateIndexSQL(IndexTransportRouteStop.values()));
stat.executeUpdate(IndexConstants.generateCreateSQL(IndexTransportStop.values()));
stat.executeUpdate(IndexConstants.generateCreateIndexSQL(IndexTransportStop.values()));
stat.execute("PRAGMA user_version = " + IndexConstants.TRANSPORT_TABLE_VERSION); //$NON-NLS-1$
stat.close();
PreparedStatement prepRoute = conn.prepareStatement(
IndexConstants.generatePrepareStatementToInsert(IndexTransportRoute.getTable(), IndexTransportRoute.values().length));
PreparedStatement prepRouteStops = conn.prepareStatement(
IndexConstants.generatePrepareStatementToInsert(IndexTransportRouteStop.getTable(), IndexTransportRouteStop.values().length));
PreparedStatement prepStops = conn.prepareStatement(
IndexConstants.generatePrepareStatementToInsert(IndexTransportStop.getTable(), IndexTransportStop.values().length));
Map<PreparedStatement, Integer> count = new HashMap<PreparedStatement, Integer>();
count.put(prepRouteStops, 0);
count.put(prepRoute, 0);
count.put(prepStops, 0);
conn.setAutoCommit(false);
Set<Long> writtenStops = new LinkedHashSet<Long>();
for(String t : region.getTransportRoutes().keySet()){
for(TransportRoute r : region.getTransportRoutes().get(t)) {
assert IndexTransportRoute.values().length == 7;
prepRoute.setLong(IndexTransportRoute.ID.ordinal() + 1, r.getId());
prepRoute.setString(IndexTransportRoute.TYPE.ordinal() + 1, r.getType());
prepRoute.setString(IndexTransportRoute.OPERATOR.ordinal() + 1, r.getOperator());
prepRoute.setString(IndexTransportRoute.REF.ordinal() + 1, r.getRef());
prepRoute.setString(IndexTransportRoute.NAME.ordinal() + 1, r.getName());
prepRoute.setString(IndexTransportRoute.NAME_EN.ordinal() + 1, r.getEnName());
prepRoute.setInt(IndexTransportRoute.DIST.ordinal() + 1, r.getAvgBothDistance());
addBatch(count, prepRoute);
writeRouteStops(prepRouteStops, prepStops, count, writtenStops, r, r.getForwardStops(), true);
writeRouteStops(prepRouteStops, prepStops, count, writtenStops, r, r.getBackwardStops(), false);
}
}
for(PreparedStatement p : count.keySet()){
if(count.get(p) > 0){
p.executeBatch();
}
p.close();
}
conn.setAutoCommit(true);
} finally {
conn.close();
log.info(String.format("Indexing transport done in %s ms.", System.currentTimeMillis() - now)); //$NON-NLS-1$
}
if(date != null){
file.setLastModified(date);
}
return this;
}
private void writeRouteStops(PreparedStatement prepRouteStops, PreparedStatement prepStops, Map<PreparedStatement, Integer> count,
Set<Long> writtenStops, TransportRoute r, List<TransportStop> stops, boolean direction) throws SQLException {
for(TransportStop s : stops){
if (!writtenStops.contains(s.getId())) {
assert IndexTransportStop.values().length == 5;
prepStops.setLong(IndexTransportStop.ID.ordinal() + 1, s.getId());
prepStops.setDouble(IndexTransportStop.LATITUDE.ordinal() + 1, s.getLocation().getLatitude());
prepStops.setDouble(IndexTransportStop.LONGITUDE.ordinal() + 1, s.getLocation().getLongitude());
prepStops.setString(IndexTransportStop.NAME.ordinal() + 1, s.getName());
prepStops.setString(IndexTransportStop.NAME_EN.ordinal() + 1, s.getEnName());
addBatch(count, prepStops);
writtenStops.add(s.getId());
}
assert IndexTransportRouteStop.values().length == 3;
prepRouteStops.setLong(IndexTransportRouteStop.ROUTE.ordinal() + 1, r.getId());
prepRouteStops.setLong(IndexTransportRouteStop.STOP.ordinal() + 1, s.getId());
prepRouteStops.setBoolean(IndexTransportRouteStop.DIRECTION.ordinal() + 1, direction);
addBatch(count, prepRouteStops);
}
}
private void addBatch(Map<PreparedStatement, Integer> count, PreparedStatement p) throws SQLException{
p.addBatch();
if(count.get(p) >= BATCH_SIZE){

View file

@ -4,14 +4,17 @@ public class IndexConstants {
// Important : Every time you change schema of db upgrade version!!!
// If you want that new application support old index : put upgrade code in android app ResourceManager
public final static int TRANSPORT_TABLE_VERSION = 0;
public final static int POI_TABLE_VERSION = 0;
public final static int ADDRESS_TABLE_VERSION = 1;
public static final String POI_INDEX_DIR = "POI/"; //$NON-NLS-1$
public static final String ADDRESS_INDEX_DIR = "Address/"; //$NON-NLS-1$
public static final String TRANSPORT_INDEX_DIR = "Transport/"; //$NON-NLS-1$
public static final String POI_INDEX_EXT = ".poi.odb"; //$NON-NLS-1$
public static final String ADDRESS_INDEX_EXT = ".addr.odb"; //$NON-NLS-1$
public static final String TRANSPORT_INDEX_EXT = ".trans.odb"; //$NON-NLS-1$
public interface IndexColumn {
public boolean isIndex();
@ -108,6 +111,7 @@ public class IndexConstants {
return b.toString();
}
// POI index
public enum IndexPoiTable implements IndexColumn {
ID("long"), LATITUDE("double", true), LONGITUDE("double", true), OPENING_HOURS, NAME, NAME_EN, TYPE, SUBTYPE; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
@ -137,7 +141,8 @@ public class IndexConstants {
return index;
}
}
// Address index
public enum IndexCityTable implements IndexColumn {
ID("long"), LATITUDE("double", true), LONGITUDE("double", true), NAME, NAME_EN, CITY_TYPE; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
@ -282,4 +287,115 @@ public class IndexConstants {
return index;
}
}
// Transport Index
public enum IndexTransportStop implements IndexColumn {
ID("long"), LATITUDE("double", true), LONGITUDE("double", true), NAME, NAME_EN; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
boolean index = false;
String type = null;
private IndexTransportStop() {
}
private IndexTransportStop(String type) {
this.type = type;
}
private IndexTransportStop(String type, boolean index) {
this(type);
this.index = index;
}
public static String getTable() {
return "transport_stop"; //$NON-NLS-1$
}
public String getTableName() {
return getTable();
}
@Override
public String getType() {
return type;
}
@Override
public boolean isIndex() {
return index;
}
}
public enum IndexTransportRouteStop implements IndexColumn {
STOP("long"), ROUTE("long", true), DIRECTION("boolean"); //$NON-NLS-1$ //$NON-NLS-2$
boolean index = false;
String type = null;
private IndexTransportRouteStop() {
}
private IndexTransportRouteStop(String type) {
this.type = type;
}
private IndexTransportRouteStop(String type, boolean index) {
this(type);
this.index = index;
}
public static String getTable() {
return "transport_route_stop"; //$NON-NLS-1$
}
public String getTableName() {
return getTable();
}
@Override
public String getType() {
return type;
}
@Override
public boolean isIndex() {
return index;
}
}
public enum IndexTransportRoute implements IndexColumn {
ID("long", true), TYPE, OPERATOR, REF, NAME, NAME_EN, DIST("int"); //$NON-NLS-1$ //$NON-NLS-2$
boolean index = false;
String type = null;
private IndexTransportRoute() {
}
private IndexTransportRoute(String type) {
this.type = type;
}
private IndexTransportRoute(String type, boolean index) {
this(type);
this.index = index;
}
public static String getTable() {
return "transport_route"; //$NON-NLS-1$
}
public String getTableName() {
return getTable();
}
@Override
public String getType() {
return type;
}
@Override
public boolean isIndex() {
return index;
}
}
}

View file

@ -12,9 +12,13 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.xml.parsers.ParserConfigurationException;
@ -323,6 +327,17 @@ public class DataExtraction {
transport.add((Relation) e);
processed = true;
}
if(e instanceof Node && "bus_stop".equals(e.getTag(OSMTagKey.HIGHWAY))){
// load all stops in order to get their names
processed = true;
}
if (e instanceof Node && e.getTag(OSMTagKey.RAILWAY) != null) {
if ("tram_stop".equals(e.getTag(OSMTagKey.RAILWAY)) || "station".equals(e.getTag(OSMTagKey.RAILWAY))) {
// load all stops in order to get their names
processed = true;
}
}
}
// put all nodes into temporary db to get only required nodes after loading all data
try {
@ -571,6 +586,19 @@ public class DataExtraction {
/// way with name : МЗОР, ул. ...,
}
private static Set<String> acceptedRoutes = new HashSet<String>();
static {
acceptedRoutes.add("bus");
acceptedRoutes.add("trolleybus");
acceptedRoutes.add("share_taxi");
acceptedRoutes.add("subway");
acceptedRoutes.add("train");
acceptedRoutes.add("tram");
acceptedRoutes.add("ferry");
}
public void readingTransport(final ArrayList<Relation> transport, Region country, IProgress progress){
progress.startTask("Reading transport...", -1);
@ -579,10 +607,18 @@ public class DataExtraction {
for(Relation rel : transport){
String ref = rel.getTag(OSMTagKey.REF);
String route = rel.getTag(OSMTagKey.ROUTE);
String operator = rel.getTag(OSMTagKey.OPERATOR);
if(route == null || ref == null){
continue;
}
String operator = rel.getTag(OSMTagKey.OPERATOR);
if(!acceptedRoutes.contains(route)){
continue;
}
TransportRoute r = new TransportRoute(rel, ref);
r.setOperator(operator);
r.setType(route);
if(operator != null){
route = operator + " : " + route;
}
@ -590,44 +626,111 @@ public class DataExtraction {
routes.put(route, new ArrayList<TransportRoute>());
}
TransportRoute r = new TransportRoute(rel, ref);
for(Entry<Entity, String> e: rel.getMemberEntities().entrySet()){
final Map<TransportStop, Integer> forwardStops = new LinkedHashMap<TransportStop, Integer>();
final Map<TransportStop, Integer> backwardStops = new LinkedHashMap<TransportStop, Integer>();
int currentStop = 0;
int forwardStop = 0;
int backwardStop = 0;
for (Entry<Entity, String> e : rel.getMemberEntities().entrySet()) {
if(e.getValue().contains("stop")){
if(e.getKey() instanceof Node){
if(!routeStops.containsKey(e.getKey().getId())){
routeStops.put(e.getKey().getId(), new TransportStop(e.getKey()));
}
TransportStop stop = routeStops.get(e.getKey().getId());
boolean forward = e.getValue().contains("forward") || !e.getValue().contains("backward");
if(forward){
boolean forward = e.getValue().contains("forward");
boolean backward = e.getValue().contains("backward");
currentStop++;
if(forward || !backward){
forwardStop ++;
}
if(backward){
backwardStop ++;
}
boolean common = !forward && !backward;
int index = -1;
int i = e.getValue().length() -1;
int accum = 1;
while(i >= 0 && Character.isDigit(e.getValue().charAt(i))){
if(index < 0){
index = 0;
}
index = accum * Character.getNumericValue(e.getValue().charAt(i)) + index;
accum *= 10;
i --;
}
if(index < 0){
index = forward ? forwardStop : (backward ? backwardStop : currentStop) ;
}
if(forward || common){
forwardStops.put(stop, index);
r.getForwardStops().add(stop);
} else {
}
if(backward || common){
if(common){
// put with negative index
backwardStops.put(stop, -index);
} else {
backwardStops.put(stop, index);
}
r.getBackwardStops().add(stop);
}
}
} else if(e.getKey() instanceof Way){
r.addWay((Way) e.getKey());
}
}
if(r.getBackwardStops().isEmpty() && !r.getForwardStops().isEmpty()){
List<TransportStop> stops = r.getBackwardStops();
for(TransportStop s : r.getForwardStops()){
stops.add(0, s);
}
} else if(!r.getForwardStops().isEmpty()){
if(r.getForwardStops().get(0) != r.getBackwardStops().get(r.getBackwardStops().size() - 1)){
r.getBackwardStops().add(r.getForwardStops().get(0));
}
} else {
if(forwardStops.isEmpty() && backwardStops.isEmpty()){
continue;
}
Collections.sort(r.getForwardStops(), new Comparator<TransportStop>(){
@Override
public int compare(TransportStop o1, TransportStop o2) {
return forwardStops.get(o1) - forwardStops.get(o2);
}
});
// all common stops are with negative index (reeval them)
for(TransportStop s : new ArrayList<TransportStop>(backwardStops.keySet())){
if(backwardStops.get(s) < 0){
backwardStops.put(s, backwardStops.size() + backwardStops.get(s) -1);
}
}
Collections.sort(r.getBackwardStops(), new Comparator<TransportStop>(){
@Override
public int compare(TransportStop o1, TransportStop o2) {
return backwardStops.get(o1) - backwardStops.get(o2);
}
});
routes.get(route).add(r);
// validate that all is ok
// if (validateTransportStops(r.getBackwardStops(), r) && validateTransportStops(r.getForwardStops(), r)) {
// System.out.println("Route " + r + " is valid ");
// }
}
progress.finishTask();
}
protected boolean validateTransportStops(List<TransportStop> stops, TransportRoute r){
boolean valid = true;
for (int i = 2; i < stops.size(); i++) {
TransportStop s1 = stops.get(i - 2);
TransportStop s2 = stops.get(i - 1);
TransportStop s3 = stops.get(i);
if (MapUtils.getDistance(s1.getLocation(), s2.getLocation()) > MapUtils.getDistance(s1.getLocation(), s3.getLocation()) * 1.3) {
System.out.println("SOMETHING WRONG with " + i + "th of " + r.getRef() +" "+ r );
valid = false;
}
}
return valid;
}
private void readingAmenities(final ArrayList<Entity> amenities, Region country) {
for(Entity a: amenities){
country.registerAmenity(new Amenity(a));
@ -733,6 +836,7 @@ public class DataExtraction {
// String path = "E:\\Information\\OSM maps\\minsk_extr.bz2";
// String path = "E:\\Information\\OSM maps\\netherlands.osm.bz2";
String wDir = "E:\\Information\\OSM maps\\osmand\\";
File f = new File(path);

View file

@ -9,6 +9,7 @@ public class OSMSettings {
HIGHWAY("highway"), //$NON-NLS-1$
BUILDING("building"), //$NON-NLS-1$
POSTAL_CODE("postal_code"), //$NON-NLS-1$
RAILWAY("railway"), //$NON-NLS-1$
// transport
ROUTE("route"), //$NON-NLS-1$
OPERATOR("operator"), //$NON-NLS-1$

View file

@ -403,7 +403,7 @@ public class OsmExtractionUI implements IMapLocationListener {
buildTransportIndex = new JCheckBox();
buildTransportIndex.setText("Build transport index");
panel.add(buildTransportIndex);
buildTransportIndex.setSelected(false);
buildTransportIndex.setSelected(true);
loadingAllData = new JCheckBox();
loadingAllData.setText("Loading all osm data");
@ -433,10 +433,12 @@ public class OsmExtractionUI implements IMapLocationListener {
if(buildAddressIndex.isSelected()){
dlg.startTask("Generating address index...", -1);
builder.writeAddress();
msg.append(", Address index ").append("successfully created");
msg.append(", address index ").append("successfully created");
}
if(buildTransportIndex.isSelected()){
// TODO
dlg.startTask("Generating transport index...", -1);
builder.writeTransport();
msg.append(", transport index ").append("successfully created");
}
// new DataIndexReader().testIndex(new File(
@ -753,6 +755,7 @@ public class OsmExtractionUI implements IMapLocationListener {
}
} catch (InterruptedException e1) {
log.error("Interrupted", e1);
updateButtonsBar();
} catch (InvocationTargetException e1) {
ExceptionHandler.handle("Exception during operation", e1.getCause());
}

View file

@ -158,7 +158,7 @@ public class OsmandSettings {
public static boolean isShowingViewAngle(Context ctx) {
SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
return prefs.getBoolean(SHOW_VIEW_ANGLE, true);
return prefs.getBoolean(SHOW_VIEW_ANGLE, false);
}
// this value string is synchronized with settings_pref.xml preference name
@ -290,7 +290,7 @@ public class OsmandSettings {
public static int getLastKnownMapZoom(Context ctx) {
SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
return prefs.getInt(LAST_KNOWN_MAP_ZOOM, 3);
return prefs.getInt(LAST_KNOWN_MAP_ZOOM, 5);
}
public static void setLastKnownMapZoom(Context ctx, int zoom) {