/*
* Copyright (C) 2014 Alec Dhuse
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.foldingmap.mapImportExport;
import co.foldingmap.map.vector.LinearRing;
import co.foldingmap.map.vector.Region;
import co.foldingmap.map.vector.MultiGeometry;
import co.foldingmap.map.vector.LatLonAltBox;
import co.foldingmap.map.vector.VectorLayer;
import co.foldingmap.map.vector.NodeMap;
import co.foldingmap.map.vector.CoordinateMath;
import co.foldingmap.map.vector.CoordinateList;
import co.foldingmap.map.vector.VectorObject;
import co.foldingmap.map.vector.VectorObjectList;
import co.foldingmap.map.vector.LineString;
import co.foldingmap.map.vector.Polygon;
import co.foldingmap.map.vector.LatLonBox;
import co.foldingmap.map.vector.Coordinate;
import co.foldingmap.map.vector.MapPoint;
import co.foldingmap.map.vector.InnerBoundary;
import co.foldingmap.GUISupport.ProgressBarPanel;
import co.foldingmap.GUISupport.ProgressIndicator;
import co.foldingmap.GUISupport.Updateable;
import co.foldingmap.Logger;
import co.foldingmap.ResourceHelper;
import co.foldingmap.dataStructures.PropertyValuePair;
import co.foldingmap.map.DigitalMap;
import co.foldingmap.map.MapProjection;
import co.foldingmap.xml.XMLTag;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
/**
* This class is used to import OpenStreetMap.org XML files. These files are
* usually downloaded from the OpenStreetMap.org website via the export option.
*
* @author Alec
*/
public class OsmImporter extends Thread {
private final DigitalMap mapData;
private final File osmFile;
private float minlat, minlon, maxlat, maxlon;
private final ProgressIndicator progressIndicator;
private final Updateable updateable;
public OsmImporter(DigitalMap mapData,
File osmFile,
Updateable updateable,
ProgressIndicator progressIndicator) {
super("OsmImporter");
this.mapData = mapData;
this.osmFile = osmFile;
this.updateable = updateable;
this.progressIndicator = progressIndicator;
}
/**
* Check custom data field to see if it has a match.
*
* @param customDataFields
* @param selectors
* @return
*/
public static boolean checkDataFieldsForMatch(HashMap<String, String> customDataFields, PropertyValuePair[] selectors) {
boolean returnValue = false;
String lookupValue = "";
if ((customDataFields != null) && (selectors != null)) {
for (PropertyValuePair selector : selectors) {
lookupValue = customDataFields.get(selector.getProperty());
if (lookupValue != null) {
if (lookupValue.equals(selector.getValue())) {
returnValue = true;
} else {
returnValue = false;
break;
}
} else {
returnValue = false;
break;
}
}
}
return returnValue;
}
/**
* Creates a KML region based on the OSM dataLevel
* This is experimental and not yet finished.
*
* @param dataLevel
* @return
*/
public Region createRegion(int dataLevel) {
float maxLevelOfDetailPixels, minLevelOfDetailPixels;
LatLonAltBox bounds;
Region newRegion;
String regionName;
newRegion = new Region("Default Region");
try {
bounds = new LatLonAltBox(minlat, maxlat, maxlon, minlon, 0, 10000000);
regionName = "OSM Region For Data Level: " + dataLevel;
switch (dataLevel) {
case 1:
maxLevelOfDetailPixels = 100000000;
minLevelOfDetailPixels = 600;
break;
default:
maxLevelOfDetailPixels = 100000000;
minLevelOfDetailPixels = 1;
}
newRegion = new Region(regionName, bounds, maxLevelOfDetailPixels, minLevelOfDetailPixels);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.createRegion(int) - " + e);
}
return newRegion;
}
/**
* Creates an OsmMember object from the OSM XML member tag.
* Returns null if a member could not be created.
*
* @param memberXML
* @return
*/
public static OsmMember getOsmMember(String memberXML) {
int start, end;
OsmMember newMember;
String ref, role, type;
newMember = null;
try {
//get member type
start = memberXML.indexOf("type=") + 6;
end = memberXML.indexOf("\"", start);
type = memberXML.substring(start, end);
//get ref ID
start = memberXML.indexOf("ref=") + 5;
end = memberXML.indexOf("\"", start);
ref = memberXML.substring(start, end);
//get role
start = memberXML.indexOf("role=") + 6;
end = memberXML.indexOf("\"", start);
role = memberXML.substring(start, end);
newMember = new OsmMember(type, ref, role);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.getOsmMember(String) - " + e);
}
return newMember;
}
/**
* Extracts a specific property from an Open Street Map xlm string
*
* @param nodeString The XML string.
* @param nodeProperty The name of the property being retrieved.
* @return String The property value.
*/
public static String getOsmNodeProperty(String nodeString, String nodeProperty) {
int tabPropertyStart;
int tabPropertyEnd = -1;
String propertyValue = "";
try {
tabPropertyStart = nodeString.indexOf(nodeProperty + "=");
if (tabPropertyStart >= 0) {
tabPropertyStart += (nodeProperty.length() + 2);
tabPropertyEnd = nodeString.indexOf("\"", tabPropertyStart);
}
if ((tabPropertyEnd == -1) && (tabPropertyStart >= 0))
tabPropertyEnd = nodeString.indexOf("'", tabPropertyStart);
if ((tabPropertyEnd >= 0) && (tabPropertyStart >= 0))
propertyValue = nodeString.substring(tabPropertyStart, tabPropertyEnd);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.getOsmNodeProperty(String, String) - " + e);
}
return propertyValue;
}
/**
* Returns a single OsmNode object given the node XML.
*
* @param nodeXML
* @return
*/
public static OsmNode getOsmNode(String nodeXML) {
ArrayList<PropertyValuePair> tagInfo;
Coordinate nodeCoordinate;
int offset;
int idStart, latStart, lonStart, tagStart;
int idEnd, latEnd, lonEnd, tagEnd;
PropertyValuePair currentTag;
String id, lat, lon, tag;
offset = 0;
tagStart = 0;
tagInfo = new ArrayList<PropertyValuePair>();
try {
idStart = nodeXML.indexOf("id=") + 4;
idEnd = nodeXML.indexOf(" ", idStart) - 1;
id = nodeXML.substring(idStart, idEnd);
latStart = nodeXML.indexOf("lat=") + 5;
latEnd = nodeXML.indexOf("\"", latStart);
if (latEnd == -1)
latEnd = nodeXML.indexOf("'", latStart);
lat = nodeXML.substring(latStart, latEnd);
lonStart = nodeXML.indexOf("lon=") + 5;
lonEnd = nodeXML.indexOf("\"", lonStart);
if (lonEnd == -1)
lonEnd = nodeXML.indexOf("'", lonStart);
lon = nodeXML.substring(lonStart, lonEnd);
nodeCoordinate = new Coordinate(0, Float.parseFloat(lat), Float.parseFloat(lon), Long.parseLong(id));
nodeCoordinate.setId(Long.parseLong(id));
while (tagStart != -1) {
tagStart = nodeXML.indexOf("<tag", offset);
tagEnd = nodeXML.indexOf("/>", tagStart) + 2;
if (tagStart >= 0 && tagEnd > 0) {
tag = nodeXML.substring(tagStart, tagEnd);
currentTag = getOsmTag(tag);
tagInfo.add(currentTag);
//set elevation if available
if (currentTag.getProperty().equalsIgnoreCase("ele")) {
try {
nodeCoordinate.setAltitude(Float.parseFloat(currentTag.getValue()));
} catch (Exception e) {
Logger.log(Logger.WARN, "Could not read elevation for node id: " + id);
}
}
}
offset = tagEnd;
}
return new OsmNode(id, nodeCoordinate, tagInfo);
} catch (NumberFormatException | ParseException e) {
Logger.log(Logger.ERR, "Error in OsmImporter.getOsmNode(String) - " + e);
return null;
}
}
/**
* Loads Open Street Map nodes into a HashMap. Node IDs are used as keys,
* while a Coordinate Object is used as the value.
*
* @param nodeXML
* @param progressPanel Panel to update progress, can be null.
* @return HashMap The HashMap containing the loaded nodes.
*/
public static HashMap<String, OsmNode> getOsmNodes(String nodeXML, ProgressBarPanel progressPanel) {
double percentDone;
HashMap<String, OsmNode> coordinateNodes;
int nodeStart, nodeEnd, nodeTagEnd, offset;
OsmNode newNode;
String nodeText;
//initilize object variables
offset = 0;
coordinateNodes = new HashMap<String, OsmNode>(10000);
try {
while (offset < nodeXML.length()) {
percentDone = (((double) offset) / ((double) nodeXML.length())) * 100;
nodeStart = nodeXML.indexOf("<node id=", offset);
nodeEnd = nodeXML.indexOf("/>", nodeStart);
nodeTagEnd = nodeXML.indexOf("</node>", nodeStart) + 7;
if (percentDone > 0 && progressPanel != null)
progressPanel.updateProgress("Loading OSM Nodes", (int) (percentDone - 21));
if ((nodeStart >= 0) && (nodeEnd > nodeStart)) {
nodeText = nodeXML.substring(nodeStart, nodeTagEnd);
} else {
//no more node tags
nodeText = null;
offset = nodeXML.length();
}
newNode = getOsmNode(nodeText);
offset = nodeTagEnd;
if (newNode != null) {
coordinateNodes.put(newNode.getNodeID(), newNode);
}
} //end while parse statment
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.getOsmNodes(String, ProgressBarPanel) - " + e);
}
return coordinateNodes;
}
/**
* Creates a MapPoint from an OsmNode.
*
* @param node
* @param nodeMap
* @return
*/
public static MapPoint getOsmPoint(OsmNode node, NodeMap nodeMap) {
ArrayList<PropertyValuePair> nodeTags;
Coordinate pointCoordinate;
HashMap<String,String> customFields;
long nodeID;
MapPoint newPoint;
String name, key, value;
try {
customFields = new HashMap<String,String>();
name = node.getNodeID();
nodeTags = node.getNodeTags();
nodeID = Long.parseLong(node.getNodeID());
pointCoordinate = nodeMap.get(nodeID);
if (pointCoordinate == null) {
Logger.log(Logger.ERR, "Could not retrieve node ID: " + node.getNodeID() + " from Node Map.");
pointCoordinate = node.getNodeCoordinate();
}
//Replace HTML safe text with normal text.
name = XMLTag.convertSafeText(name);
//add a tag to say it came from OSM
key = "DataSource";
value = "OpenStreetMap.org";
customFields.put(key, value);
for (PropertyValuePair currentPair: nodeTags) {
if (currentPair.getProperty().equalsIgnoreCase("Name")) {
name = currentPair.getValue();
} else {
key = currentPair.getProperty();
value = currentPair.getValue();
customFields.put(key, value);
}
}
newPoint = new MapPoint(name, pointCoordinate, customFields);
//apply theme
if (customFields.containsKey("aeroway")) {
value = customFields.get("aeroway");
if (value.equalsIgnoreCase("aerodrome")) {
newPoint.setClass("Airport");
} else if (value.equalsIgnoreCase("aeroway")) {
newPoint.setClass("Airport");
}
} else if (customFields.containsKey("amenity")) {
value = customFields.get("amenity");
if (value.equalsIgnoreCase("arts_centre")) {
newPoint.setClass("Art Gallery");
} else if (value.equalsIgnoreCase("bank")) {
newPoint.setClass("Bank");
} else if (value.equalsIgnoreCase("bar")) {
newPoint.setClass("Bar");
} else if (value.equalsIgnoreCase("bus_station")) {
newPoint.setClass("Bus Station");
} else if (value.equalsIgnoreCase("cafe")) {
newPoint.setClass("Cafe");
} else if (value.equalsIgnoreCase("cinema")) {
newPoint.setClass("Cinema");
} else if (value.equalsIgnoreCase("courthouse")) {
newPoint.setClass("Courthouse");
} else if (value.equalsIgnoreCase("fast_food")) {
newPoint.setClass("Restaurant - Fast Food");
} else if (value.equalsIgnoreCase("fire_station")) {
newPoint.setClass("Fire Station");
} else if (value.equalsIgnoreCase("fuel")) {
newPoint.setClass("Gas Station");
} else if (value.equalsIgnoreCase("grave_yard")) {
newPoint.setClass("Gas Station");
} else if (value.equalsIgnoreCase("hospital")) {
newPoint.setClass("Hospital");
} else if (value.equalsIgnoreCase("library")) {
newPoint.setClass("Library");
} else if (value.equalsIgnoreCase("place_of_worship")) {
if (customFields.containsKey("religion")) {
value = customFields.get("religion");
if (value.equalsIgnoreCase("christian")) {
newPoint.setClass("Place Of Worship - Christian");
} else if (value.equalsIgnoreCase("hindu")) {
newPoint.setClass("Place Of Worship - Hindu");
} else if (value.equalsIgnoreCase("islam")) {
newPoint.setClass("Place Of Worship - Islam");
} else if (value.equalsIgnoreCase("jewish")) {
newPoint.setClass("Place Of Worship - Jewish");
} else {
newPoint.setClass("Place Of Worship");
}
}
} else if (value.equalsIgnoreCase("pharmacy")) {
newPoint.setClass("Pharmacy");
} else if (value.equalsIgnoreCase("police")) {
newPoint.setClass("Police Station");
} else if (value.equalsIgnoreCase("post_office")) {
newPoint.setClass("Post Office");
} else if (value.equalsIgnoreCase("pub")) {
newPoint.setClass("Bar");
} else if (value.equalsIgnoreCase("restaurant")) {
newPoint.setClass("Restaurant");
} else if (value.equalsIgnoreCase("school")) {
newPoint.setClass("School");
} else if (value.equalsIgnoreCase("university")) {
newPoint.setClass("University");
} else {
newPoint.setClass("Amenity");
}
} else if (customFields.containsKey("building")) {
value = customFields.get("building");
if (value.equalsIgnoreCase("yes")) {
newPoint.setClass("Building");
}
} else if (customFields.containsKey("bus")) {
value = customFields.get("bus");
if (value.equalsIgnoreCase("yes"))
newPoint.setClass("Bus Station");
} else if (customFields.containsKey("landuse")) {
value = customFields.get("landuse");
if (value.equalsIgnoreCase("mine")) {
newPoint.setClass("Mine");
}
} else if (customFields.containsKey("leisure")) {
value = customFields.get("leisure");
if (value.equalsIgnoreCase("park")) {
newPoint.setClass("Park");
} else if (value.equalsIgnoreCase("playground")) {
newPoint.setClass("Park");
}
} else if (customFields.containsKey("natural")) {
value = customFields.get("natural");
if (value.equalsIgnoreCase("peak")) {
newPoint.setClass("Mountain Peak");
}
} else if (customFields.containsKey("highway")) {
value = customFields.get("highway");
if (value.equalsIgnoreCase("bus_stop")) {
newPoint.setClass("Bus Station");
}
} else if (customFields.containsKey("historic")) {
value = customFields.get("historic");
if (value.equalsIgnoreCase("memorial")) {
newPoint.setClass("Memorial");
}
} else if (customFields.containsKey("man_made")) {
value = customFields.get("man_made");
if (value.equalsIgnoreCase("tower")) {
newPoint.setClass("Antenna");
}
} else if (customFields.containsKey("parking")) {
value = customFields.get("parking");
if (value.equalsIgnoreCase("multi-storey")) {
newPoint.setClass("Parking Garage");
} else {
newPoint.setClass("Parking");
}
} else if (customFields.containsKey("place")) {
value = customFields.get("place");
if (value.equalsIgnoreCase("city")) {
newPoint.setClass("City");
} else if (value.equalsIgnoreCase("hamlet")) {
newPoint.setClass("Place - Village");
} else if (value.equalsIgnoreCase("suburb")) {
newPoint.setClass("Place - Suburb");
} else if (value.equalsIgnoreCase("town")) {
newPoint.setClass("Place - Town");
} else if (value.equalsIgnoreCase("village")) {
newPoint.setClass("Place - Village");
}
} else if (customFields.containsKey("railway")) {
value = customFields.get("railway");
if (value.equalsIgnoreCase("stop")) {
newPoint.setClass("Railway Stop");
} else if (value.equalsIgnoreCase("tram_stop")) {
newPoint.setClass("Railway Stop");
}
} else if (customFields.containsKey("shop")) {
value = customFields.get("shop");
if (value.equalsIgnoreCase("supermarket")) {
newPoint.setClass("Super Market");
} else {
newPoint.setClass("Shop");
}
} else if (customFields.containsKey("tourism")) {
if (value.equalsIgnoreCase("hotel")) {
newPoint.setClass("Hotel");
} else if (value.equalsIgnoreCase("camp_site")) {
newPoint.setClass("Camp Site");
} else {
newPoint.setClass("Tourist Attraction");
}
}
return newPoint;
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.getOsmPoint(OsmNode) - " + e);
}
return null;
}
/**
* Creates a PropertyValuePair from an OSM XML String of a tag.
*
* @param tagXMLString
* @return
* @throws ParseException
*/
public static PropertyValuePair getOsmTag(String tagXMLString) throws ParseException {
int keyStart, keyEnd, valueStart, valueEnd;
PropertyValuePair returnData;
String property, value;
returnData = null;
try {
//find the key aka property
keyStart = tagXMLString.indexOf("k=", 0) + 3;
keyEnd = tagXMLString.indexOf("\"", keyStart);
if (keyEnd < 0)
keyEnd = tagXMLString.indexOf("'", keyStart);
property = tagXMLString.substring(keyStart, keyEnd);
//find the value
valueStart = tagXMLString.indexOf("v=", 0) + 3;
valueEnd = tagXMLString.indexOf("\"", valueStart);
if (valueEnd < 0)
valueEnd = tagXMLString.indexOf("'", valueStart);
value = tagXMLString.substring(valueStart, valueEnd);
returnData = new PropertyValuePair(property, value);
} catch (Exception e) {
throw new ParseException("Can not parse OSM Tag", 0);
}
return returnData;
}
/**
* Returns a list of PropertyValuePairs created from an OSM XML String
* of tags.
*
* @param tagsXMLString
* @return
*/
public static ArrayList<PropertyValuePair> getOsmTags(String tagsXMLString) {
ArrayList<PropertyValuePair> dataPairs;
int offset, tagStart, tagEnd;
PropertyValuePair currentPropertyValuePair;
String tagXML;
dataPairs = new ArrayList<PropertyValuePair>();
offset = 0;
while (offset < tagsXMLString.length()) {
tagStart = tagsXMLString.indexOf("<tag", offset);
tagEnd = tagsXMLString.indexOf("/>", tagStart) + 2;
if (tagStart >= 0) {
tagXML = tagsXMLString.substring(tagStart, tagEnd);
try {
currentPropertyValuePair = getOsmTag(tagXML);
dataPairs.add(currentPropertyValuePair);
} catch (ParseException pe) {
Logger.log(Logger.ERR, "Could not parse line: " + tagXML);
}
offset = tagEnd;
} else {
break;
}
}
return dataPairs;
}
/**
* Creates a LineString or Polygon from an OSM XML String way.
*
* @param wayXML
* @param nodeMap
* @return
*/
public static VectorObject getOsmWay(String wayXML, NodeMap nodeMap) {
boolean isCliff, isPolygon, isRing;
Coordinate tempCoordinate;
CoordinateList<Coordinate> objectCoordinates;
HashMap<String, String> customDataFields;
int dataLevel, offset, propertyTagXmlEnd, tabPropertyStart, tabPropertyEnd;
VectorObject newMapObject;
PropertyValuePair property;
Region objectRegion;
String objectColor;
String nodeID, wayHighway, wayID, wayName, waySurface, wayTimestamp, wayTrackType;
String currentTag, polygonType, propertyTagXML, value, wayType, naturalType;
StringBuilder objectDescription;
//initilize
customDataFields = new HashMap<String, String>();
isCliff = false;
isPolygon = false;
isRing = false;
newMapObject = null;
wayName = "";
waySurface = "";
wayTrackType = "";
objectColor = "";
objectCoordinates = new CoordinateList<Coordinate>();
objectRegion = null;
offset = 0;
polygonType = "(Unspecified Polygon)";
objectDescription = new StringBuilder();
wayType = "(Unspecified Linestring)";
//load values
wayID = getOsmNodeProperty(wayXML, "id"); //way id
wayTimestamp = getOsmNodeProperty(wayXML, "timestamp"); //timestamp
offset = wayXML.indexOf(">", offset) + 2;
//add a tag to say it came from OSM
customDataFields.put("DataSource", "OpenStreetMap.org");
//add a tag for the OSM Way ID
customDataFields.put("OsmID", wayID);
try {
while (offset < wayXML.length()) {
//read way properties and componet nodes
tabPropertyStart = wayXML.indexOf("<", offset);
tabPropertyEnd = wayXML.indexOf("=", tabPropertyStart);
if ( (tabPropertyStart >= 0) && (tabPropertyEnd >= 0) ) {
currentTag = wayXML.substring( (tabPropertyStart + 1), (tabPropertyEnd));
if (currentTag.equalsIgnoreCase("nd ref")) {
//componet node
tabPropertyStart = wayXML.indexOf("=", tabPropertyStart);
tabPropertyEnd = wayXML.indexOf("/", tabPropertyStart);
nodeID = wayXML.substring( (tabPropertyStart + 2), (tabPropertyEnd - 1));
if (nodeID.endsWith("'"))
nodeID = nodeID.substring(0, nodeID.length() - 1);
tempCoordinate = nodeMap.get(Long.parseLong(nodeID));
if (tempCoordinate == null) {
//cannot find node
Logger.log(Logger.ERR, "OsmImporter.getOsmWay() - Can't find Node: " + nodeID);
} else {
//Add coordinate to vector to eventualy put in new VectorObject, force add to make sure loops are closed.
objectCoordinates.forceAdd(tempCoordinate);
}
} else if (currentTag.equalsIgnoreCase("tag k")) {
//property
propertyTagXmlEnd = tabPropertyEnd = wayXML.indexOf("/>", tabPropertyStart) + 2;
propertyTagXML = wayXML.substring((tabPropertyStart - 1), propertyTagXmlEnd);
property = getOsmTag(propertyTagXML);
//add the tag to the custom field properties
customDataFields.put(property.getProperty(), property.getValue());
if (property.getProperty().equalsIgnoreCase("name")) {
wayName = XMLTag.convertSafeText(property.getValue());
} else if (property.getProperty().equalsIgnoreCase("admin_level")) {
wayType = "Territorial Boundary";
} else if (property.getProperty().equalsIgnoreCase("amenity")) {
if (property.getValue().equalsIgnoreCase("fast_food")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("fountain")) {
polygonType = "Lake";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("marketplace")) {
polygonType = "Market";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("parking")) {
polygonType = "Parking";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("place_of_worship")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("post_office")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("pub")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("restaurant")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("school")) {
polygonType = "School";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("toilets")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("university")) {
polygonType = "University";
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("aeroway")) {
if (property.getValue().equalsIgnoreCase("helipad")) {
polygonType = "Parking Lot";
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("area")) {
if (property.getValue().equalsIgnoreCase("yes")) {
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("border_type")) {
if (property.getValue().equalsIgnoreCase("territorial")) {
wayName = "Border";
}
} else if (property.getProperty().equalsIgnoreCase("boundary")) {
if (property.getValue().equalsIgnoreCase("administrative")) {
wayType = "Border - Inter-Country";
} else if (property.getValue().equalsIgnoreCase("national_park")) {
polygonType = "Protected Area";
} else if (property.getValue().equalsIgnoreCase("protected_area")) {
polygonType = "Protected Area";
} else if (property.getValue().equalsIgnoreCase("town")) {
polygonType = "Country - Filled";
} else {
wayType = "Territorial Boundary";
}
} else if (property.getProperty().equalsIgnoreCase("building")) {
if (property.getValue().equalsIgnoreCase("yes")) {
polygonType = "Building";
} else {
polygonType = "Building";
}
isPolygon = true;
} else if (property.getProperty().equalsIgnoreCase("color")) {
objectColor = property.getValue();
if (isCliff == true) {
if (objectColor.equals("Brown")) {
polygonType = "Rock - Sandstone";
} else if (objectColor.equals("Red")) {
polygonType = "Rock - Sandstone Red";
} else if (objectColor.equals("White")) {
polygonType = "Rock - Sandstone White";
}
}
} else if (property.getProperty().equalsIgnoreCase("DataLevel")) {
dataLevel = Integer.parseInt(property.getValue());
} else if (property.getProperty().equalsIgnoreCase("footway")) {
wayType = "Path - Footway";
} else if (property.getProperty().equalsIgnoreCase("highway")) {
wayHighway = property.getValue();
//parse diferent types of roads
if (wayHighway.equalsIgnoreCase("bus_stop")) {
} else if (wayHighway.equalsIgnoreCase("construction")) {
} else if (wayHighway.equalsIgnoreCase("cycleway")) {
wayType = "Path - Bikeway";
} else if (wayHighway.equalsIgnoreCase("footway")) {
wayType = "Hiking Trail";
} else if (wayHighway.equalsIgnoreCase("living_street")) {
wayType = "Road - City Tertiary";
} else if (wayHighway.equalsIgnoreCase("mini_roundabout")) {
} else if (wayHighway.equalsIgnoreCase("motorway")) {
wayType = "Road - Motorway";
} else if (wayHighway.equalsIgnoreCase("motorway_link")) {
wayType = "Road - Motorway Link";
} else if (wayHighway.equalsIgnoreCase("path")) {
wayType = "Hiking Trail";
} else if (wayHighway.equalsIgnoreCase("pedestrian")) {
wayType = "Hiking Trail";
} else if (wayHighway.equalsIgnoreCase("primary")) {
wayType = "Road - Primary Highway";
} else if (wayHighway.equalsIgnoreCase("primary_link")) {
wayType = "Road - Primary Highway Link";
} else if (wayHighway.equalsIgnoreCase("residential")) {
wayType = "Road - City Secondary";
} else if (wayHighway.equalsIgnoreCase("road")) {
wayType = "Road - City Secondary";
} else if (wayHighway.equalsIgnoreCase("secondary")) {
wayType = "Road - Secondary Highway";
} else if (wayHighway.equalsIgnoreCase("secondary_link")) {
wayType = "Road - Secondary Highway Link";
} else if (wayHighway.equalsIgnoreCase("service")) {
wayType = "Road - City Tertiary";
} else if (wayHighway.equalsIgnoreCase("steps")) {
wayType = "Path - Steps";
} else if (wayHighway.equalsIgnoreCase("tertiary")) {
wayType = "Road - City Tertiary";
} else if (wayHighway.equalsIgnoreCase("tertiary_link")) {
wayType = "Road - City Tertiary";
} else if (wayHighway.equalsIgnoreCase("track")) {
wayType = "Road - Track";
} else if (wayHighway.equalsIgnoreCase("trunk")) {
wayType = "Road - Secondary Highway";
} else if (wayHighway.equalsIgnoreCase("trunk_link")) {
wayType = "Road - Secondary Highway Link";
} else if (wayHighway.equalsIgnoreCase("unclassified")) {
wayType = "Road - Unclassified";
}
} else if (property.getProperty().equalsIgnoreCase("landuse")) {
if (property.getValue().equalsIgnoreCase("commercial")) {
polygonType = "Commercial Area";
} else if (property.getValue().equalsIgnoreCase("conservation")) {
polygonType = "Protected Area";
} else if (property.getValue().equalsIgnoreCase("gated_community")) {
polygonType = "Residential Area";
} else if (property.getValue().equalsIgnoreCase("grass")) {
polygonType = "Grass Field";
} else if (property.getValue().equalsIgnoreCase("farm")) {
polygonType = "Agricultural Plot";
} else if (property.getValue().equalsIgnoreCase("farmland")) {
polygonType = "Agricultural Plot";
} else if (property.getValue().equalsIgnoreCase("field")) {
polygonType = "Agricultural Plot";
} else if (property.getValue().equalsIgnoreCase("forest")) {
polygonType = "Forest";
} else if (property.getValue().equalsIgnoreCase("industrial")) {
polygonType = "Industrial Area";
} else if (property.getValue().equalsIgnoreCase("meadow")) {
polygonType = "Grass Field";
} else if (property.getValue().equalsIgnoreCase("recreation_ground")) {
polygonType = "Grass Field";
} else if (property.getValue().equalsIgnoreCase("reservoir")) {
polygonType = "Lake";
wayType = "Coastline";
} else if (property.getValue().equalsIgnoreCase("residential")) {
polygonType = "Residential Area";
}
isPolygon = true;
} else if (property.getProperty().equalsIgnoreCase("leisure")) {
if (property.getValue().equalsIgnoreCase("common")) {
polygonType = "Park";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("garden")) {
polygonType = "Grass Field";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("golf_course")) {
polygonType = "Grass Field";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("marina")) {
polygonType = "Lake";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("park")) {
polygonType = "Park";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("pitch")) {
polygonType = "Sports Field";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("playground")) {
polygonType = "Park";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("recreation_ground")) {
polygonType = "Sports Field";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("shop")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("stadium")) {
polygonType = "Stadium";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("swimming_pool")) {
polygonType = "Lake";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("track")) {
wayType = "Path - Running";
isPolygon = false;
isRing = true;
}
} else if (property.getProperty().equalsIgnoreCase("man_made")) {
if (property.getValue().equalsIgnoreCase("pier")) {
wayType = "Pier";
} else if (property.getValue().equalsIgnoreCase("water_tower")) {
isPolygon = true;
polygonType = "Building";
} else if (property.getValue().equalsIgnoreCase("wastewater_plant")) {
isPolygon = true;
polygonType = "Industrial Area";
}
} else if (property.getProperty().equalsIgnoreCase("MP_TYPE")) {
if (property.getValue().equalsIgnoreCase("0x00")) {
} else if (property.getValue().equalsIgnoreCase("0x02")) {
isPolygon = false;
wayType = "Road - Unclassified";
} else if (property.getValue().equalsIgnoreCase("0x3C")) {
polygonType = "Lake";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("0x41")) {
polygonType = "Lake";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("0x45")) {
polygonType = "Lake";
isPolygon = true;
} else if (property.getValue().equalsIgnoreCase("0x50")) {
polygonType = "Forest";
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("natural")) {
naturalType = property.getValue();
if (naturalType.equalsIgnoreCase("beach")) {
polygonType = "Beach";
isPolygon = true;
} else if (naturalType.equalsIgnoreCase("cliff")) {
if (objectColor.length() > 0) {
isPolygon = true;
isCliff = true;
if (objectColor.equals("brown")) {
polygonType = "Rock - Sandstone";
} else if (objectColor.equals("red")) {
polygonType = "Rock - Sandstone Red";
} else if (objectColor.equals("white")) {
polygonType = "Rock - Sandstone White";
}
}
} else if (naturalType.equalsIgnoreCase("coastline")) {
wayType = "Coastline";
polygonType = "Island";
//If coastline end points are close together, assume an island
if (CoordinateMath.getDistance(objectCoordinates.get(0), objectCoordinates.lastCoordinate()) < 200)
isPolygon = true;
} else if (naturalType.equalsIgnoreCase("heath")) {
polygonType = "Land Cover - Heath";
isPolygon = true;
} else if (naturalType.equalsIgnoreCase("reef")) {
polygonType = "Reef";
isPolygon = true;
} else if (naturalType.equalsIgnoreCase("rock")) {
polygonType = "Rock - Granite";
isCliff = true;
isPolygon = true;
} else if (naturalType.equalsIgnoreCase("rocks")) {
polygonType = "Rock - Granite";
isCliff = true;
isPolygon = true;
} else if (naturalType.equalsIgnoreCase("wadi")) {
polygonType = "Water - Wadi";
wayType = "Water Way - Intermittent Stream";
} else if (naturalType.equalsIgnoreCase("water")) {
wayType = "Water Way - River";
polygonType = "Lake";
} else if (naturalType.equalsIgnoreCase("wood")) {
polygonType = "Forest";
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("parking")) {
polygonType = "Parking Lot";
isPolygon = true;
} else if (property.getProperty().equalsIgnoreCase("place")) {
value = property.getValue();
if (value.equalsIgnoreCase("island")) {
polygonType = "Small Island";
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("power")) {
value = property.getValue();
if (value.equalsIgnoreCase("line")) {
wayType = "Power Line";
isPolygon = false;
} else if (value.equalsIgnoreCase("sub_station")) {
wayType = "Industrial Area";
isPolygon = true;
}
} else if (property.getProperty().equalsIgnoreCase("railway")) {
value = property.getValue();
if (value.equalsIgnoreCase("light_rail")) {
wayType = "Rail - Tram";
} else if (value.equalsIgnoreCase("platform")) {
wayType = "Rail - Platform";
} else if (value.equalsIgnoreCase("tram")) {
wayType = "Rail - Tram";
} else {
wayType = "Rail Line";
}
} else if (property.getProperty().equalsIgnoreCase("ref")) {
} else if (property.getProperty().equalsIgnoreCase("route")) {
if (property.getValue().equalsIgnoreCase("ferry")) {
wayType = "Ferry Line";
}
} else if (property.getProperty().equalsIgnoreCase("shop")) {
polygonType = "Building";
isPolygon = true;
} else if (property.getProperty().equalsIgnoreCase("source")) {
} else if (property.getProperty().equalsIgnoreCase("sport")) {
polygonType = "Stadium";
isPolygon = true;
} else if (property.getProperty().equalsIgnoreCase("surface")) {
waySurface = property.getValue();
} else if (property.getProperty().equalsIgnoreCase("tracktype")) {
wayTrackType = property.getValue();
} else if (property.getProperty().equalsIgnoreCase("waterway")) {
wayType = property.getValue();
if (wayType.equalsIgnoreCase("river")) {
wayType = "Water Way - River";
polygonType = "River";
} else if(wayType.equalsIgnoreCase("riverbank")) {
polygonType = "River";
isPolygon = true;
} else if(wayType.equalsIgnoreCase("stream")) {
wayType = "Water Way - Stream";
isPolygon = false;
} else if(wayType.equalsIgnoreCase("wadi")) {
polygonType = "Water - Wadi";
wayType = "Water Way - Intermittent Stream";
} else {
wayType = "Water Way - River";
polygonType = "Lake";
}
} else if (property.getProperty().equalsIgnoreCase("width")) {
} else {
} //end tag type if
} //end way tag type if
offset = tabPropertyEnd + 1;
} else { //end prop start and end check
//break loop
offset = wayXML.length();
}
} //end while loop
//Experiment with new class creation system
if (checkDataFieldsForMatch(customDataFields, new PropertyValuePair[]{new PropertyValuePair("waterway", "stream"), new PropertyValuePair("intermittent", "yes")})) {
polygonType = "Water - Wadi";
wayType = "Water Way - Intermittent Stream";
}
//add newly constructed object
if (objectCoordinates.size() > 1) {
if (objectCoordinates.get(0) == objectCoordinates.get(objectCoordinates.size() - 1)) {
//closed object, if not a road or Boundary create polygon
if ((wayType.contains("Road")) || wayType.contains("Boundary") || wayType.contains("Border")) {
newMapObject = new LinearRing(wayName, wayType, objectCoordinates);
} else {
objectCoordinates.remove(0); //Polygon Objects do not contain the same start and end coordinate, but OSM does, remove to prevent conflict
newMapObject = new Polygon(wayName, polygonType, objectCoordinates);
}
} else if (isRing) {
newMapObject = new LinearRing(wayName, wayType, objectCoordinates);
} else if (isPolygon) {
newMapObject = new Polygon(wayName, polygonType, objectCoordinates);
} else {
//unclosed object create linestring
newMapObject = new LineString(wayName, wayType, objectCoordinates);
}
newMapObject.setCustomDataFields(customDataFields);
newMapObject.setReference(Long.parseLong(wayID));
newMapObject.setDescription(objectDescription.toString());
} else if (objectCoordinates.size() == 1) {
//create a point
Logger.log(Logger.ERR, "Way has only one point");
newMapObject = new MapPoint(wayName, polygonType, objectDescription.toString(), objectCoordinates.get(0));
} else {
Logger.log(Logger.ERR, "No Nodes found for OSM Way - " + wayXML);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OSMImporter.getOsmWay(String, NodeMap) - " + e);
}
return newMapObject;
}
/**
* Creates MultiGeometry from OSM relations.
* TODO: Needs work on the merging of LineString
*
* @param importLayer
* @param xml
* @param objects
* @return
*/
public static VectorObject getRelation(VectorLayer importLayer, String xml, HashMap<String, VectorObject> objects) {
boolean mergeToPolygon;
CoordinateList<Coordinate> newCoordinates;
HashMap<String, String> customDataFields;
int memberStart, memberEnd;
int tagStart, tagEnd;
int offset;
VectorObject currentObject;
MultiGeometry relation;
PropertyValuePair tag;
String memberText, memberType, refID, tagText;
String polygonType, lineType;
relation = new MultiGeometry("New Relation");
try {
offset = 0;
tagStart = xml.indexOf("<tag");
customDataFields = new HashMap<String, String>();
polygonType = "";
lineType = "";
mergeToPolygon = false;
//Get relation members
while (offset < tagStart) {
memberStart = xml.indexOf("<member ", offset);
memberEnd = xml.indexOf("/>", memberStart) + 2;
if (memberStart >= 0 && memberEnd > 0) {
memberText = xml.substring(memberStart, memberEnd);
offset = memberEnd;
refID = getRelationMemberProperty(memberText, "ref");
memberType = getRelationMemberProperty(memberText, "type");
currentObject = objects.get(refID);
if (currentObject != null) {
relation.addObject(currentObject);
if (!(currentObject instanceof MultiGeometry))
importLayer.removeObject(currentObject);
}
} else {
offset = tagStart;
}
}//member loop end
//get relation properties
while (offset < xml.length()) {
tagStart = xml.indexOf("<tag ", offset);
tagEnd = xml.indexOf("/>", tagStart) + 2;
if (tagStart >= 0 && tagEnd > 0) {
tagText = xml.substring(tagStart, tagEnd);
offset = tagEnd;
tag = getOsmTag(tagText);
customDataFields.put(tag.getProperty(), tag.getValue());
if (tag.getProperty().equalsIgnoreCase("name")) {
relation.setName(tag.getValue());
} else if (tag.getProperty().equalsIgnoreCase("landuse")) {
if (tag.getValue().equalsIgnoreCase("reservoir")) {
polygonType = "River";
lineType = "Water Way - River";
mergeToPolygon = true;
}
} else if (tag.getProperty().equalsIgnoreCase("natural")) {
if (tag.getValue().equalsIgnoreCase("cliff")) {
lineType = "Rock Face";
} else if (tag.getValue().equalsIgnoreCase("wadi")) {
polygonType = "Water - Wadi";
lineType = "Water Way - Intermittent Stream";
} else if (tag.getValue().equalsIgnoreCase("water")) {
polygonType = "River";
lineType = "Water Way - River";
}
} else if (tag.getProperty().equalsIgnoreCase("place")) {
if (tag.getValue().equalsIgnoreCase("island")) {
mergeToPolygon = true;
polygonType = "Small Island";
}
} else if (tag.getProperty().equalsIgnoreCase("type")) {
if (tag.getValue().equalsIgnoreCase("multipolygon")) {
mergeToPolygon = true;
} else if (tag.getValue().equalsIgnoreCase("watershed")) {
polygonType = "River";
lineType = "Water Way - River";
}
}
} else {
offset = xml.length();
}
}
//assign typs to objects
VectorObjectList<VectorObject> componetObjects = relation.getComponentObjects();
newCoordinates = new CoordinateList<Coordinate>();
for (int i = 0; i < componetObjects.size(); i++) {
VectorObject o = componetObjects.get(i);
if (o instanceof Polygon && !polygonType.equals("")) {
o.setClass(polygonType);
} else if (o instanceof LineString && !lineType.equals("")) {
o.setClass(lineType);
}
}
//merge lineStrings into a single lineString or Polygon
CoordinateList<Coordinate> newObjectCoordinates;
VectorObjectList<VectorObject> mergedObjects;
newObjectCoordinates = null;
mergedObjects = new VectorObjectList<VectorObject>();
for (VectorObject object: componetObjects) {
if (object instanceof LineString) {
if (newObjectCoordinates != null) {
if (newObjectCoordinates.lastCoordinate() == object.getCoordinateList().get(0)) {
newObjectCoordinates.addAll(object.getCoordinateList());
mergedObjects.add(object);
} else if (newObjectCoordinates.lastCoordinate() == object.getCoordinateList().lastCoordinate()) {
CoordinateList<Coordinate> reversed = object.getCoordinateList();
reversed.reverse();
newObjectCoordinates.addAll(reversed);
mergedObjects.add(object);
}
} else {
//not initilized, use firt objec's CoordinateList
newObjectCoordinates = object.getCoordinateList();
mergedObjects.add(object);
}
}
}
if (mergedObjects.size() == componetObjects.size()) {
//All componet Objects are mergable, create a new object to replace
if (mergeToPolygon) {
Polygon newPoly = new Polygon(relation.getName(), polygonType, newObjectCoordinates);
newPoly.setCustomDataFields(customDataFields);
return newPoly;
} else {
LineString newLine = new LineString(relation.getName(), lineType, newObjectCoordinates);
newLine.setCustomDataFields(customDataFields);
return newLine;
}
} else {
//only merge some of the componets
//removed the object we are merging
for (VectorObject mergedObject: mergedObjects)
relation.getComponentObjects().remove(mergedObject);
LineString newLine = new LineString(mergedObjects.get(0).getName(), lineType, newObjectCoordinates);
relation.addObject(newLine);
}
relation.setCustomDataFields(customDataFields);
//PropertyValuePair getOsmTag
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.getRelation(String, HashMap) - " + e);
}
return relation;
}
/**
* Return the property from an xml string of the form:
* <member type="way" ref="32122400" role="outer"/>
*
* @param xml
* @param property
* @return
*/
private static String getRelationMemberProperty(String xml, String property) {
int refStart, refEnd;
String value;
value = "";
refStart = xml.indexOf(property) + 2 + property.length();
refEnd = xml.indexOf("\"", refStart);
if (refStart > 0 && refEnd > 0)
value = xml.substring(refStart, refEnd);
return value;
}
/**
* Imports an OpenStreeMap.org map
*/
private VectorObjectList<VectorObject> importOSM(String xml, ProgressBarPanel progressPanel) {
ArrayList<OsmNode> nodes;
HashMap<String, OsmNode> coordinateNodes;
int nodeEnd, nodeStart, offset;
int boundsStart, boundsEnd;
VectorObject newMapObject;
String mapBoundsXML, nodeTags, wayTag;
VectorObjectList<VectorObject> mapObjects;
mapObjects = new VectorObjectList<VectorObject>();
offset = 0;
try {
//read header info
boundsStart = xml.indexOf("<bounds ");
boundsEnd = xml.indexOf("/>", boundsStart) + 2;
if (boundsStart >= 0 && boundsEnd >= 0) {
mapBoundsXML = xml.substring(boundsStart, boundsEnd);
parseMapBounds(mapBoundsXML);
}
//locate node sarts and end
int nodesStart = xml.indexOf("<node");
int nodesEnd = xml.lastIndexOf("</node>") + 7;
int lastNodeStart = xml.lastIndexOf("<node") + 1;
if (lastNodeStart > nodesEnd)
nodesEnd = xml.indexOf("/>", lastNodeStart) + 2;
if (progressPanel != null)
progressPanel.updateProgress("Loading OSM Nodes", 1);
if ((nodesStart >= 0) && (nodesStart < nodesEnd)) {
nodeTags = xml.substring(nodesStart, nodesEnd);
coordinateNodes = getOsmNodes(nodeTags, progressPanel);
xml = xml.substring(nodesStart - 1); //trim to alow larger files
//load points
if (progressPanel != null)
progressPanel.updateProgress("Loading OSM Points", 85);
nodes = new ArrayList<OsmNode>(coordinateNodes.values());
for (OsmNode currentNode: nodes) {
if (currentNode.hasNameTag()) {
mapObjects.add(getOsmPoint(currentNode, mapData.getCoordinateSet()));
}
}
//load ways
if (progressPanel != null)
progressPanel.updateProgress("Loading OSM Ways", 90);
while (offset < xml.length()) {
nodeStart = xml.indexOf("<way id=", offset);
nodeEnd = xml.indexOf("</way>", nodeStart);
if ((nodeStart >= 0) && (nodeEnd >= 0)) {
wayTag = xml.substring(nodeStart, nodeEnd);
newMapObject = getOsmWay(wayTag, mapData.getCoordinateSet());
if (newMapObject != null)
mapObjects.add(newMapObject);
offset = nodeEnd + 6;
} else {
offset = xml.length();
}
} // while loop
} //end node start / end posistion check
if (progressPanel != null)
progressPanel.updateProgress("Finishing Import", 99);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error on OsmImporter.importOSM(String, ProgressBarPanel) - " + e);
}
return mapObjects;
}
/**
* Tries to merge connected coastlines together.
*
* @param importLayer
* @param coastlines
*/
public static void mergeCoastlines(VectorLayer importLayer, ArrayList<VectorObject> coastlines) {
ArrayList<VectorObject> objectMerged, objectsToRemove;
Coordinate mainStart, mainEnd;
Coordinate matchStart, matchEnd;
CoordinateList<Coordinate> cList;
float maxDistance, distance;
Polygon newPoly;
VectorObject mainObject, matchingObject;
objectMerged = new ArrayList<VectorObject>();
objectsToRemove = new ArrayList<VectorObject>();
for (int i = 0; i < coastlines.size(); i++) {
mainObject = coastlines.get(i);
for (int j = 0; j < coastlines.size(); j++) {
matchingObject = coastlines.get(j);
if (mainObject != matchingObject &&
!objectsToRemove.contains(mainObject) &&
!objectsToRemove.contains(matchingObject)) {
mainStart = mainObject.getCoordinateList().get(0);
mainEnd = mainObject.getCoordinateList().lastCoordinate();
matchStart = matchingObject.getCoordinateList().get(0);
matchEnd = matchingObject.getCoordinateList().lastCoordinate();
if (mainEnd.equals(matchStart)) {
objectsToRemove.add(matchingObject);
cList = matchingObject.getCoordinateList();
cList.remove(matchStart); //remove redundant coordinate
mainObject.getCoordinateList().addAll(cList);
}
if (mainStart.equals(matchEnd)) {
objectsToRemove.add(matchingObject);
cList = matchingObject.getCoordinateList().clone();
cList.remove(matchEnd); //remove redundant coordinate
cList.addAll(mainObject.getCoordinateList());
mainObject.setCoordinateList(cList);
}
if (mainStart.equals(matchStart)) {
System.out.println("MainStart - MatchStart");
}
if (mainEnd.equals(matchEnd)) {
objectsToRemove.add(matchingObject);
cList = matchingObject.getCoordinateList().clone();
cList.remove(matchEnd); //remove redundant coordinate
cList.reverse();
mainObject.getCoordinateList().addAll(cList);
}
}
}
objectMerged.add(mainObject);
}
//remove object that have been added to other objects.
for (VectorObject object: objectsToRemove) {
importLayer.removeObject(object);
coastlines.remove(object);
}
//check the distance between the first and last coordinate in the list
for (VectorObject object: coastlines) {
cList = object.getCoordinateList();
distance = CoordinateMath.getDistance(cList.get(0), cList.lastCoordinate());
maxDistance = CoordinateMath.getMaxDistances(cList) * 2;
if (distance < maxDistance) {
//the distance is within an acceptable margin merge to a polygon
newPoly = new Polygon(object.getName(), "Small Island", object.getCoordinateList());
newPoly.addCustomDataFields(object.getAllCustomData());
importLayer.addObject(newPoly);
importLayer.removeObject(object);
}
}
//sort imported objects
importLayer.getObjectList().sortByLayer();
}
/**
* Processes OSM relations and transforms already loaded object accordingly.
* OSM relation build polygons, specify restrictions and add other info.
*
* @param objects
* @param importLayer
* @param relation
*/
public static void processRelation(HashMap<String, VectorObject> objects,
VectorLayer importLayer,
OsmRelation relation) {
CoordinateList<Coordinate> coordinates;
InnerBoundary innerBoundary;
int outerRolesCount;
long[] ids;
Polygon polygon;
String objectClass;
VectorObject object;
objectClass = "";
try {
if (relation.getType().equalsIgnoreCase("boundary")) {
} else if (relation.getType().equalsIgnoreCase("multipolygon")) {
outerRolesCount = relation.countOuterRoles();
if (outerRolesCount == 1) {
object = objects.get(Long.toString(relation.getOuterRoles()[0]));
if (object instanceof Polygon) {
polygon = (Polygon) object;
} else {
//object is not polygon, convert it to one
polygon = new Polygon(object.getName(), object.getObjectClass(), object.getCoordinateList());
polygon.setDescription(object.getDescription());
polygon.setCustomDataFields(object.getCustomDataFields());
}
// //add internal polygon boundaries
// ids = relation.getInnerRoles();
//
// for(long id: ids) {
// object = objects.get(Long.toString(id));
// coordinates = object.getCoordinateList();
// innerBoundary = new InnerBoundary(coordinates);
// polygon.addInnerBoundary(innerBoundary);
// importLayer.removeObject(object);
// }
} else if (outerRolesCount > 1) {
ids = relation.getOuterRoles();
object = null;
for (int i = 0; i < outerRolesCount; i++) {
object = objects.get(Long.toString(ids[i]));
if (object != null) {
objectClass = object.getObjectClass();
break;
}
}
if (object != null) {
coordinates = new CoordinateList<Coordinate>();
for(long id: ids) {
object = objects.get(Long.toString(id));
if (object != null) {
coordinates.addAll(object.getCoordinateList());
importLayer.removeObject(object);
}
}
polygon = new Polygon(relation.getPropertyValue("name"), objectClass, coordinates);
polygon.addCustomDataFields(relation.getProperties());
importLayer.addObject(polygon);
}
}
} else if (relation.getType().equalsIgnoreCase("restriction")) {
} else if (relation.getType().equalsIgnoreCase("route")) {
String name = relation.getPropertyValue("name");
MultiGeometry mg = new MultiGeometry(name);
ArrayList<OsmMember> members = relation.getMembers();
for (OsmMember om: members) {
VectorObject vObj = objects.get(Long.toString(om.refID));
if (vObj != null) {
importLayer.removeObject(vObj);
mg.addObject(vObj);
} else {
Logger.log(Logger.ERR, "Error in OsmImporter.processRelation(HashMap, VectorLayer, OsmRelation) - Null object member id: " + om.refID);
}
}
importLayer.addObject(mg);
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.processRelation(HashMap, VectorLayer, OsmRelation) - " + e);
}
}
/**
* Sets the map bounds for the map being imported.
*
* @param mapBoundsXML
*/
public void parseMapBounds(String mapBoundsXML) {
int boundsPropStart, boundsPropEnd;
String mapBoundsProp;
try {
boundsPropStart = mapBoundsXML.indexOf("minlat=") + 8;
boundsPropEnd = mapBoundsXML.indexOf("\"", boundsPropStart);
if (boundsPropEnd < 0) {
boundsPropEnd = mapBoundsXML.indexOf("'", boundsPropStart);
}
mapBoundsProp = mapBoundsXML.substring(boundsPropStart, boundsPropEnd);
minlat = Float.parseFloat(mapBoundsProp);
boundsPropStart = mapBoundsXML.indexOf("minlon=") + 8;
boundsPropEnd = mapBoundsXML.indexOf("\"", boundsPropStart);
if (boundsPropEnd < 0) {
boundsPropEnd = mapBoundsXML.indexOf("'", boundsPropStart);
}
mapBoundsProp = mapBoundsXML.substring(boundsPropStart, boundsPropEnd);
minlon = Float.parseFloat(mapBoundsProp);
boundsPropStart = mapBoundsXML.indexOf("maxlat=") + 8;
boundsPropEnd = mapBoundsXML.indexOf("\"", boundsPropStart);
if (boundsPropEnd < 0) {
boundsPropEnd = mapBoundsXML.indexOf("'", boundsPropStart);
}
mapBoundsProp = mapBoundsXML.substring(boundsPropStart, boundsPropEnd);
maxlat = Float.parseFloat(mapBoundsProp);
boundsPropStart = mapBoundsXML.indexOf("maxlon=") + 8;
boundsPropEnd = mapBoundsXML.indexOf("\"", boundsPropStart);
if (boundsPropEnd < 0) {
boundsPropEnd = mapBoundsXML.indexOf("'", boundsPropStart);
}
mapBoundsProp = mapBoundsXML.substring(boundsPropStart, boundsPropEnd);
maxlon = Float.parseFloat(mapBoundsProp);
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OSMImporter.parseMapBounds(String) - " + e);
}
}
@Override
public void run() {
try {
ArrayList<VectorObject> coastlines;
boolean nodesCompleted, nodeStarted;
boolean waysCompleted, wayStarted, relationStarted;
BufferedReader br;
float lat, lon, radius, zoom;
HashMap<String, VectorObject> objectIDs;
LatLonAltBox bounds;
VectorObject currentObject;
MapProjection mapProjection;
OsmNode osmNode;
String line;
StringBuffer nodeXML, wayXML, relationXML;
VectorLayer newLayer;
progressIndicator.updateProgress("Opening OSM File", 1);
br = new BufferedReader(new FileReader(osmFile));
coastlines = new ArrayList<VectorObject>();
line = "";
newLayer = new VectorLayer("OSM Import");
nodeXML = new StringBuffer();
objectIDs = new HashMap<String, VectorObject>(10000);
relationXML = new StringBuffer();
wayXML = new StringBuffer();
nodesCompleted = false;
nodeStarted = false;
waysCompleted = false;
wayStarted = false;
relationStarted = false;
newLayer.setParentMap(mapData);
while (!line.contains("</osm>")) {
while (br.ready()) {
line = br.readLine().trim();
if (nodesCompleted == false) {
if (line.startsWith("<node") && !line.endsWith("/>")) {
nodeXML = new StringBuffer();
nodeStarted = true;
nodeXML.append(line);
nodeXML.append("\n");
} else if (line.startsWith("<node") && line.endsWith("/>")) {
osmNode = OsmImporter.getOsmNode(line);
if (osmNode != null) {
mapData.getCoordinateSet().put(osmNode.getNodeCoordinate());
}
} else if (line.startsWith("</node>")) {
nodeXML.append(line);
osmNode = OsmImporter.getOsmNode(nodeXML.toString());
nodeStarted = false;
if (osmNode != null) {
mapData.getCoordinateSet().put(osmNode.getNodeCoordinate());
if (osmNode.hasNameTag()) {
currentObject = OsmImporter.getOsmPoint(osmNode, mapData.getCoordinateSet());
if (osmNode != null) {
newLayer.addObject(currentObject);
objectIDs.put(currentObject.getCustomDataFieldValue("OsmID"), currentObject);
if (updateable != null) updateable.update();
}
}
}
}
}
if (waysCompleted == false && nodeStarted == false) {
if (line.startsWith("<way ")) {
nodesCompleted = true;
wayXML = new StringBuffer();
wayStarted = true;
wayXML.append(line);
wayXML.append("\n");
} else if (line.startsWith("</way>")) {
wayXML.append(line);
currentObject = OsmImporter.getOsmWay(wayXML.toString(), mapData.getCoordinateSet());
if (currentObject != null) {
objectIDs.put(currentObject.getCustomDataFieldValue("OsmID"), currentObject);
newLayer.addObject(currentObject);
if (updateable != null) updateable.update();
wayStarted = false;
if (currentObject.getObjectClass().equalsIgnoreCase("Coastline"))
coastlines.add(currentObject);
} else {
Logger.log(Logger.ERR, "Could Not Covert OSM Way - " + wayXML.toString());
}
}
}
if (nodesCompleted == true) {
if (line.startsWith("<relation ")) {
waysCompleted = true;
relationXML = new StringBuffer();
relationStarted = true;
relationXML.append(line);
relationXML.append("\n");
} else if (line.startsWith("</relation>")) {
relationStarted = false;
relationXML.append(line);
//This needs more work before use
OsmImporter.processRelation(objectIDs, newLayer, OsmRelation.parseRelation(relationXML.toString()));
} else {
if (nodeStarted) {
nodeXML.append(line);
nodeXML.append("\n");
} else if (wayStarted) {
wayXML.append(line);
wayXML.append("\n");
} else if (relationStarted) {
relationXML.append(line);
relationXML.append("\n");
}
}
}
} //end of osm loop
}
progressIndicator.updateProgress("Cleaning up", 95);
OsmImporter.mergeCoastlines(newLayer, coastlines);
mapData.addLayer(newLayer, 0);
//set view
bounds = mapData.getBoundary();
mapProjection = mapData.getLastMapView().getMapProjection();
lon = bounds.getWest() + (bounds.getWidth() / 4f);
lat = bounds.getNorth() - (bounds.getHeight() / 4f);
mapData.setLookAtCoordinate(new Coordinate(0, lat, lon));
float southY = (float) mapProjection.getY(bounds.getSouthWestCoordinate());
if (southY < 1) {
mapProjection.setZoomLevel(southY * 600);
} else {
mapProjection.setZoomLevel(southY * 6);
}
if (progressIndicator != null) {
progressIndicator.updateProgress("Done", 100);
progressIndicator.finish();
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in OsmImporter.run() - " + e);
}
}
/**
* Searches the OSM database for a boundary matching the query String.
*
* @param query
*/
public static BoundsSearchResult[] search(String query) {
ArrayList<BoundsSearchResult> searchResults;
BoundsSearchResult searchResult;
StringBuilder sb = new StringBuilder();
sb.append("http://nominatim.openstreetmap.org/search/");
query = query.replaceAll(" ", "%20");
query = query.replaceAll("\t", "%20");
sb.append(query);
sb.append("?format=xml");
sb.append("&polygon=1");
String result = ResourceHelper.downloadString(sb.toString());
//Interpret results
int offset = 0;
int bboxStartIndex, bboxEndIndex, placeIndex;
int nameStartIndex, nameEndIndex;
float north, south, east, west;
LatLonBox bounds;
String bbox, name;
StringTokenizer st;
searchResults = new ArrayList<BoundsSearchResult>();
placeIndex = result.indexOf("<place", offset);
offset = placeIndex;
while (placeIndex > 0 && offset < result.length()) {
bboxStartIndex = result.indexOf("boundingbox=", offset) + 13;
bboxEndIndex = result.indexOf("\"", bboxStartIndex);
bbox = result.substring(bboxStartIndex, bboxEndIndex);
offset = bboxEndIndex;
nameStartIndex = result.indexOf("display_name=", offset) + 14;
nameEndIndex = result.indexOf("'", nameStartIndex);
offset = nameEndIndex;
name = result.substring(nameStartIndex, nameEndIndex);
placeIndex = result.indexOf("<place", offset);
offset = placeIndex;
st = new StringTokenizer(bbox, ",");
south = Float.parseFloat(st.nextToken());
north = Float.parseFloat(st.nextToken());
west = Float.parseFloat(st.nextToken());
east = Float.parseFloat(st.nextToken());
bounds = new LatLonBox(north, south, east, west);
searchResult = new BoundsSearchResult(name, bounds);
if (!searchResults.contains(searchResult))
searchResults.add(searchResult);
}
return searchResults.toArray(new BoundsSearchResult[1]);
}
}