/*
* 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.GUISupport.ProgressBarPanel;
import co.foldingmap.Logger;
import co.foldingmap.MainWindow;
import co.foldingmap.map.DigitalMap;
import co.foldingmap.map.vector.LatLonBox;
import co.foldingmap.map.vector.NodeMap;
import co.foldingmap.map.vector.VectorLayer;
import co.foldingmap.map.vector.VectorObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Connector to download OSM XML from http://www.overpass-api.de/
*
* @author Alec
*/
public class OsmOverpassDownloader extends Thread {
private ArrayList<OsmImportCondition> importConditions;
private ArrayList<VectorObject> coastlines;
private HttpURLConnection osmConnection;
private DigitalMap mapData;
private LatLonBox bounds;
private MainWindow mainWindow;
private NodeMap nodeMap;
private ProgressBarPanel progressPanel;
public OsmOverpassDownloader(MainWindow mainWindow, DigitalMap mapData, LatLonBox bounds) {
this.mainWindow = mainWindow;
this.mapData = mapData;
this.bounds = bounds;
this.importConditions = new ArrayList<OsmImportCondition>();
this.nodeMap = mapData.getCoordinateSet();
this.coastlines = new ArrayList<VectorObject>();
if (nodeMap == null) {
nodeMap = new NodeMap();
mapData.setCoordinateSet(nodeMap);
}
}
/**
* Download data from the Overpass API
*
* @param importLayer
*/
public void downloadData(VectorLayer importLayer) {
boolean nodeStarted, wayStarted, relationStarted;
BufferedReader br;
HashMap<String, OsmNode> nodes;
HashMap<String, VectorObject> objectIDs;
int idEnd, idStart, linesRead;
VectorObject currentObject;
OsmNode osmNode;
OsmRelation osmRelation;
String address, apiURL, bounding, importType, line, tags;
String north, south, east, west;
StringBuffer nodeXML, wayXML;
URL url;
north = Float.toString(bounds.getNorth());
south = Float.toString(bounds.getSouth());
east = Float.toString(bounds.getEast());
west = Float.toString(bounds.getWest());
apiURL = "http://www.overpass-api.de/api/xapi?";
bounding = "[bbox=" + west + "," + south + "," + east + "," + north + "]";
if (importConditions.isEmpty())
importConditions.add(new OsmImportCondition("Any", "Any", "Any"));
for (OsmImportCondition condition: importConditions) {
//Set the condition object type
if (condition.getObjectType().equalsIgnoreCase("Any")) {
importType = "*";
} else if (condition.getObjectType().equalsIgnoreCase("Nodes")) {
importType = "node";
} else if (condition.getObjectType().equalsIgnoreCase("Ways")) {
importType = "way";
} else if (condition.getObjectType().equalsIgnoreCase("Areas")) {
importType = "area";
} else if (condition.getObjectType().equalsIgnoreCase("Relations")) {
importType = "relation";
} else {
importType = condition.getObjectType();
}
//create tags
if (!condition.getKey().equalsIgnoreCase("Any")) {
if (condition.getValue().equalsIgnoreCase("Any")) {
tags = "[" + condition.getKey() + "=*]";
} else if (condition.getValue().equalsIgnoreCase("Any")) {
tags = "[" + condition.getKey() + "=*]";
} else {
tags = "[" + condition.getKey() + "=" + condition.getValue() + "]";
}
} else {
tags = "";
}
//Create the address
address = apiURL + importType + bounding + tags;
try {
line = "";
linesRead = 0;
url = new URL(address);
osmConnection = (HttpURLConnection) url.openConnection();
nodes = new HashMap<String, OsmNode>();
objectIDs = new HashMap<String, VectorObject>();
osmRelation = null;
nodeXML = new StringBuffer();
wayXML = new StringBuffer();
nodeStarted = false;
wayStarted = false;
relationStarted = false;
osmConnection.setReadTimeout(180*1000);
osmConnection.connect();
br = new BufferedReader(new InputStreamReader(osmConnection.getInputStream()));
while (!line.contains("</osm>")) {
while (br.ready()) {
line = br.readLine().trim();
linesRead++;
progressPanel.updateProgress("Downloading... " + Integer.toString(linesRead), 25);
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);
nodes.put(osmNode.getNodeID(), osmNode);
nodeMap.put(Long.parseLong(osmNode.getNodeID()), osmNode.getNodeCoordinate());
} else if (line.startsWith("</node>")) {
nodeXML.append(line);
osmNode = OsmImporter.getOsmNode(nodeXML.toString());
nodeStarted = false;
nodes.put(osmNode.getNodeID(), osmNode);
nodeMap.put(Long.parseLong(osmNode.getNodeID()), osmNode.getNodeCoordinate());
if (osmNode.hasNameTag()) {
currentObject = OsmImporter.getOsmPoint(osmNode, nodeMap);
importLayer.addObject(currentObject);
objectIDs.put(currentObject.getCustomDataFieldValue("OsmID"), currentObject);
mainWindow.repaint();
}
} else if (line.startsWith("<way ")) {
wayXML = new StringBuffer();
wayStarted = true;
wayXML.append(line);
wayXML.append("\n");
} else if (line.startsWith("</way>")) {
wayXML.append(line);
currentObject = OsmImporter.getOsmWay(wayXML.toString(), nodeMap);
if (currentObject != null) {
if (currentObject.getObjectClass().equalsIgnoreCase("Coastline")) {
coastlines.add(currentObject);
}
objectIDs.put(currentObject.getCustomDataFieldValue("OsmID"), currentObject);
importLayer.addObject(currentObject);
} else {
Logger.log(Logger.ERR, "Could Not Covert OSM Way - " + wayXML.toString());
}
mainWindow.repaint();
wayStarted = false;
} else if (line.startsWith("<relation ")) {
relationStarted = true;
idStart = line.indexOf("id=") + 4;
idEnd = line.indexOf("\"", idStart);
osmRelation = new OsmRelation(Long.parseLong(line.substring(idStart, idEnd)));
} else if (line.startsWith("</relation>")) {
relationStarted = false;
OsmImporter.processRelation(objectIDs, importLayer, osmRelation);
} else {
if (nodeStarted) {
nodeXML.append(line);
nodeXML.append("\n");
} else if (wayStarted) {
wayXML.append(line);
wayXML.append("\n");
} else if (relationStarted) {
if (line.startsWith("<member ")) {
osmRelation.addMember(OsmImporter.getOsmMember(line));
} else if (line.startsWith("<tag ")) {
osmRelation.addProperty(OsmImporter.getOsmTag(line));
}
}
}
} //end of osm loop
}
//OsmImporter.mergeCoastlines(importLayer, coastlines); //removed, causing issue with relations.
} catch (Exception e) {
System.err.println("Error in OsmOverpassDownloader.downloadData() - " + e);
progressPanel.setError(e.toString());
//TODO: Put a popup if there is an error
}
}
}
@Override
public void run() {
VectorLayer importLayer;
progressPanel = mainWindow.getProgressBarPanel();
importLayer = new VectorLayer("OSM Import");
progressPanel.updateProgress("Contacting OSM Server", 10);
progressPanel.setVisible(true);
mapData.addLayer(importLayer, 0);
mainWindow.updateLayersTree();
downloadData(importLayer);
importLayer.getObjectList().sortByLayer();
progressPanel.updateProgress("OSM Import Complete", 100);
progressPanel.finish();
}
/**
* Sets the import conditions for this download
* @param importConditions
*/
public void setImportConditions(ArrayList<OsmImportCondition> importConditions) {
this.importConditions = importConditions;
}
}