package de.westnordost.streetcomplete.data.osm.download;
import android.util.LongSparseArray;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import de.westnordost.streetcomplete.data.osm.ElementGeometry;
import de.westnordost.osmapi.ApiResponseReader;
import de.westnordost.osmapi.common.OsmXmlDateFormat;
import de.westnordost.osmapi.common.XmlParser;
import de.westnordost.osmapi.map.MapDataFactory;
import de.westnordost.osmapi.map.data.Element;
import de.westnordost.osmapi.map.data.LatLon;
import de.westnordost.osmapi.map.data.Node;
import de.westnordost.osmapi.map.data.OsmLatLon;
import de.westnordost.osmapi.map.data.Relation;
import de.westnordost.osmapi.map.data.RelationMember;
import de.westnordost.osmapi.map.data.Way;
/** A map data parser that also parses the geometry of elements. (Overpass parameter "geom")*/
public class OverpassMapDataParser extends XmlParser implements ApiResponseReader<Void>, WayGeometrySource
{
private static final String
NODE = "node",
WAY = "way",
RELATION = "relation",
MEMBER = "member",
ND = "nd",
TAG = "tag";
private final OsmXmlDateFormat dateFormat = new OsmXmlDateFormat();
private final ElementGeometryCreator elementGeometryCreator;
private final MapDataFactory factory;
private MapDataWithGeometryHandler handler;
private long id;
private int version;
private Double lat;
private Double lon;
private Map<String, String> tags;
private List<RelationMember> members;
private List<Long> nodes;
private LongSparseArray<List<LatLon>> nodePositionsByWay;
private List<LatLon> wayNodes;
public OverpassMapDataParser(
ElementGeometryCreator elementGeometryCreator,
MapDataFactory factory)
{
this.factory = factory;
this.elementGeometryCreator = elementGeometryCreator;
this.elementGeometryCreator.setWayGeometryProvider(this);
}
void setHandler(MapDataWithGeometryHandler handler)
{
this.handler = handler;
}
@Override
public Void parse(InputStream in)
{
if(handler == null) throw new NullPointerException();
id = -1;
version = 0;
doParse(in);
return null;
}
@Override
protected void onStartElement() throws ParseException
{
String name = getName();
switch (name)
{
case TAG:
if (tags == null)
{
tags = new HashMap<>();
}
tags.put(getAttribute("k"), getAttribute("v"));
break;
case ND:
Long ndRef = getLongAttribute("ref");
if(ndRef != null) // null for ND nodes in MEMBER
{
nodes.add(ndRef);
}
LatLon pos = new OsmLatLon(getDoubleAttribute("lat"), getDoubleAttribute("lon"));
wayNodes.add(pos);
break;
case MEMBER:
long ref = getLongAttribute("ref");
String role = getAttribute("role");
Element.Type type = Element.Type.valueOf(getAttribute("type").toUpperCase(Locale.UK));
members.add(factory.createRelationMember(ref, role, type));
startWayGeometry(ref);
break;
case NODE:
retrieveIdAndVersion();
lat = getDoubleAttribute("lat");
lon = getDoubleAttribute("lon");
break;
case WAY:
retrieveIdAndVersion();
nodes = new ArrayList<>();
nodePositionsByWay = new LongSparseArray<>();
startWayGeometry(id);
break;
case RELATION:
retrieveIdAndVersion();
members = new ArrayList<>();
nodePositionsByWay = new LongSparseArray<>();
break;
}
}
private void retrieveIdAndVersion()
{
id = getLongAttribute("id");
version = getIntAttribute("version");
}
private void startWayGeometry(long wayId)
{
wayNodes = new ArrayList<>();
nodePositionsByWay.put(wayId, wayNodes);
}
@Override
protected void onEndElement()
{
String name = getName();
Element element = null;
ElementGeometry geometry = null;
switch(name)
{
case MEMBER:
wayNodes = null;
break;
case NODE:
Node node = factory.createNode(id, version, lat, lon, tags, null, null);
geometry = elementGeometryCreator.create(node);
element = node;
break;
case WAY:
Way way = factory.createWay(id, version, nodes, tags, null, null);
geometry = elementGeometryCreator.create(way);
element = way;
nodes = null;
nodePositionsByWay = null;
wayNodes = null;
break;
case RELATION:
Relation relation = factory.createRelation(id, version, members, tags, null, null);
geometry = elementGeometryCreator.create(relation);
element = relation;
members = null;
nodePositionsByWay = null;
break;
}
if(element != null)
{
tags = null;
handler.handle(element, geometry);
}
}
@Override public List<LatLon> getNodePositions(long wayId)
{
return nodePositionsByWay.get(wayId);
}
}