package net.osmand.osm.io; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.GZIPInputStream; import net.osmand.IProgress; import net.osmand.PlatformUtil; import net.osmand.osm.edit.Entity; import net.osmand.osm.edit.Entity.EntityId; import net.osmand.osm.edit.Entity.EntityType; import net.osmand.osm.edit.EntityInfo; import net.osmand.osm.edit.Node; import net.osmand.osm.edit.Relation; import net.osmand.osm.edit.Way; import org.xml.sax.SAXException; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; public class OsmBaseStorage { protected static final String ELEM_OSM = "osm"; //$NON-NLS-1$ protected static final String ELEM_OSMCHANGE = "osmChange"; //$NON-NLS-1$ protected static final String ELEM_NODE = "node"; //$NON-NLS-1$ protected static final String ELEM_TAG = "tag"; //$NON-NLS-1$ protected static final String ELEM_WAY = "way"; //$NON-NLS-1$ protected static final String ELEM_ND = "nd"; //$NON-NLS-1$ protected static final String ELEM_RELATION = "relation"; //$NON-NLS-1$ protected static final String ELEM_MEMBER = "member"; //$NON-NLS-1$ protected static final String ELEM_MODIFY = "modify"; //$NON-NLS-1$ protected static final String ELEM_CREATE = "create"; //$NON-NLS-1$ protected static final String ELEM_DELETE = "delete"; //$NON-NLS-1$ protected static final String ATTR_VERSION = "version"; //$NON-NLS-1$ protected static final String ATTR_ID = "id"; //$NON-NLS-1$ protected static final String ATTR_LAT = "lat"; //$NON-NLS-1$ protected static final String ATTR_LON = "lon"; //$NON-NLS-1$ protected static final String ATTR_TIMESTAMP = "timestamp"; //$NON-NLS-1$ protected static final String ATTR_UID = "uid"; //$NON-NLS-1$ protected static final String ATTR_USER = "user"; //$NON-NLS-1$ protected static final String ATTR_VISIBLE = "visible"; //$NON-NLS-1$ protected static final String ATTR_CHANGESET = "changeset"; //$NON-NLS-1$ protected static final String ATTR_K = "k"; //$NON-NLS-1$ protected static final String ATTR_V = "v"; //$NON-NLS-1$ protected static final String ATTR_TYPE = "type"; //$NON-NLS-1$ protected static final String ATTR_REF = "ref"; //$NON-NLS-1$ protected static final String ATTR_ROLE = "role"; //$NON-NLS-1$ protected Entity currentParsedEntity = null; protected int currentModify = 0; protected EntityInfo currentParsedEntityInfo = null; protected boolean parseStarted; protected Map<EntityId, Entity> entities = new LinkedHashMap<EntityId, Entity>(); protected Map<EntityId, EntityInfo> entityInfo = new LinkedHashMap<EntityId, EntityInfo>(); // this is used to show feedback to user protected int progressEntity = 0; protected IProgress progress; protected InputStream inputStream; protected InputStream streamForProgress; protected List<IOsmStorageFilter> filters = new ArrayList<IOsmStorageFilter>(); protected boolean supressWarnings = true; protected boolean convertTagsToLC = true; protected boolean parseEntityInfo; public static void main(String[] args) throws IOException, SAXException, XmlPullParserException { GZIPInputStream is = new GZIPInputStream( new FileInputStream("/Users/victorshcherb/osmand/temp/m.m001508233.osc.gz")); new OsmBaseStorage().parseOSM(is, IProgress.EMPTY_PROGRESS); } public synchronized void parseOSM(InputStream stream, IProgress progress, InputStream streamForProgress, boolean entityInfo) throws IOException, XmlPullParserException { this.inputStream = stream; this.progress = progress; parseEntityInfo = entityInfo; if(streamForProgress == null){ streamForProgress = inputStream; } this.streamForProgress = streamForProgress; parseStarted = false; entities.clear(); this.entityInfo.clear(); if(progress != null){ progress.startWork(streamForProgress.available()); } XmlPullParser parser = PlatformUtil.newXMLPullParser(); parser.setInput(stream, "UTF-8"); int tok; while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) { if (tok == XmlPullParser.START_TAG) { startElement(parser, parser.getName()); } else if (tok == XmlPullParser.END_TAG) { endElement(parser, parser.getName()); } } if(progress != null){ progress.finishTask(); } completeReading(); } /** * @param stream * @throws IOException * @throws SAXException - could be */ public synchronized void parseOSM(InputStream stream, IProgress progress) throws IOException, XmlPullParserException { parseOSM(stream, progress, null, true); } public void setConvertTagsToLC(boolean convertTagsToLC) { this.convertTagsToLC = convertTagsToLC; } public boolean isSupressWarnings() { return supressWarnings; } public void setSupressWarnings(boolean supressWarnings) { this.supressWarnings = supressWarnings; } private boolean osmChange; public boolean isOsmChange() { return osmChange; } protected Long parseId(XmlPullParser parser, String name, long defId){ long id = defId; String value = parser.getAttributeValue("",name); try { id = Long.parseLong(value); } catch (NumberFormatException e) { } return id; } protected double parseDouble(XmlPullParser parser, String name, double defVal){ double ret = defVal; String value = parser.getAttributeValue("", name); if(value == null) { return defVal; } try { ret = Double.parseDouble(value); } catch (NumberFormatException e) { } return ret; } protected static final Set<String> supportedVersions = new HashSet<String>(); static { supportedVersions.add("0.6"); //$NON-NLS-1$ supportedVersions.add("0.5"); //$NON-NLS-1$ } protected void initRootElement(XmlPullParser parser, String name) throws OsmVersionNotSupported { if ((!ELEM_OSM.equals(name) && !ELEM_OSMCHANGE.equals(name)) || !supportedVersions.contains(parser.getAttributeValue("", ATTR_VERSION))) { throw new OsmVersionNotSupported(); } osmChange = ELEM_OSMCHANGE.equals(name); parseStarted = true; } protected static final int moduleProgress = 1 << 10; public void startElement(XmlPullParser parser, String name) { if(!parseStarted){ initRootElement(parser, name); } if (ELEM_MODIFY.equals(name) ) { currentModify = Entity.MODIFY_MODIFIED; } else if (ELEM_CREATE.equals(name) ) { currentModify = Entity.MODIFY_CREATED; } else if (ELEM_DELETE.equals(name) ) { currentModify = Entity.MODIFY_DELETED; } else if (currentParsedEntity == null) { progressEntity ++; if(progress != null && ((progressEntity % moduleProgress) == 0) && !progress.isIndeterminate() && streamForProgress != null){ try { progress.remaining(streamForProgress.available()); } catch (IOException e) { progress.startWork(-1); } } if (ELEM_NODE.equals(name)) { currentParsedEntity = new Node(parseDouble(parser, ATTR_LAT, 0), parseDouble(parser, ATTR_LON, 0), parseId(parser, ATTR_ID, -1)); } else if (ELEM_WAY.equals(name)) { currentParsedEntity = new Way(parseId(parser, ATTR_ID, -1)); } else if (ELEM_RELATION.equals(name)) { currentParsedEntity = new Relation(parseId(parser, ATTR_ID, -1)); } else { // this situation could be logged as unhandled } if (currentParsedEntity != null) { currentParsedEntity.setModify(currentModify); if (parseEntityInfo) { currentParsedEntityInfo = new EntityInfo(); currentParsedEntityInfo.setChangeset(parser.getAttributeValue("",ATTR_CHANGESET)); currentParsedEntityInfo.setTimestamp(parser.getAttributeValue("",ATTR_TIMESTAMP)); currentParsedEntityInfo.setUser(parser.getAttributeValue("",ATTR_USER)); currentParsedEntityInfo.setVersion(parser.getAttributeValue("",ATTR_VERSION)); currentParsedEntityInfo.setVisible(parser.getAttributeValue("",ATTR_VISIBLE)); currentParsedEntityInfo.setUid(parser.getAttributeValue("",ATTR_UID)); } } } else { if (ELEM_TAG.equals(name)) { String key = parser.getAttributeValue("",ATTR_K); if(key != null){ if(convertTagsToLC) { currentParsedEntity.putTag(key, parser.getAttributeValue("",ATTR_V)); } else { currentParsedEntity.putTagNoLC(key, parser.getAttributeValue("",ATTR_V)); } } } else if (ELEM_ND.equals(name)) { Long id = parseId(parser, ATTR_REF, -1); if(id != -1 && currentParsedEntity instanceof Way){ ((Way)currentParsedEntity).addNode(id); } } else if (ELEM_MEMBER.equals(name)) { try { Long id = parseId(parser, ATTR_REF, -1); if (id != -1 && currentParsedEntity instanceof Relation) { EntityType type = EntityType.valueOf(parser.getAttributeValue("",ATTR_TYPE).toUpperCase()); ((Relation) currentParsedEntity).addMember(id, type, parser.getAttributeValue("",ATTR_ROLE)); } } catch (Exception e) { e.printStackTrace(); } } else { // this situation could be logged as unhandled } } } public void endElement(XmlPullParser parser, String name) { EntityType type = null; if (ELEM_NODE.equals(name)){ type = EntityType.NODE; } else if (ELEM_WAY.equals(name)){ type = EntityType.WAY; } else if (ELEM_RELATION.equals(name)){ type = EntityType.RELATION; } else if (ELEM_MODIFY.equals(name) ) { currentModify = 0; } else if (ELEM_CREATE.equals(name) ) { currentModify = 0; } else if (ELEM_DELETE.equals(name) ) { currentModify = 0; } if (type != null) { if(currentParsedEntity != null){ EntityId entityId = new EntityId(type, currentParsedEntity.getId()); if(acceptEntityToLoad(entityId, currentParsedEntity)){ Entity oldEntity = entities.put(entityId, currentParsedEntity); if(parseEntityInfo && currentParsedEntityInfo != null){ entityInfo.put(entityId, currentParsedEntityInfo); } if(!supressWarnings && oldEntity!= null){ throw new UnsupportedOperationException("Entity with id=" + oldEntity.getId() +" is duplicated in osm map"); //$NON-NLS-1$ //$NON-NLS-2$ } } else { // System.gc(); } currentParsedEntity = null; } } } public void registerEntity(Entity entity, EntityInfo info) { entities.put(EntityId.valueOf(entity), entity); if (info != null) { entityInfo.put(EntityId.valueOf(entity), info); } } protected boolean acceptEntityToLoad(EntityId entityId, Entity entity) { for(IOsmStorageFilter f : filters){ if(!f.acceptEntityToLoad(this, entityId, entity)){ return false; } } return true; } public void completeReading(){ for(Entity e : entities.values()){ e.initializeLinks(entities); } } public Map<EntityId, EntityInfo> getRegisteredEntityInfo() { return entityInfo; } public Map<EntityId, Entity> getRegisteredEntities() { return entities; } public List<IOsmStorageFilter> getFilters() { return filters; } /** * Thrown when version is not supported */ public static class OsmVersionNotSupported extends RuntimeException { private static final long serialVersionUID = -127558215143984838L; } }