// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.actions.downloadtasks;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.DataSource;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.gui.PleaseWaitRunnable;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.BoundingBoxDownloader;
import org.openstreetmap.josm.io.OsmServerLocationReader;
import org.openstreetmap.josm.io.OsmServerReader;
import org.openstreetmap.josm.io.OsmTransferCancelledException;
import org.openstreetmap.josm.io.OsmTransferException;
import org.xml.sax.SAXException;
/**
* Open the download dialog and download the data.
* Run in the worker thread.
*/
public class DownloadOsmTask extends AbstractDownloadTask {
private static final Logger logger = Logger.getLogger(DownloadOsmTask.class.getName());
private Bounds currentBounds;
private DataSet downloadedData;
private DownloadTask downloadTask;
private void rememberDownloadedData(DataSet ds) {
this.downloadedData = ds;
}
public DataSet getDownloadedData() {
return downloadedData;
}
public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
downloadTask = new DownloadTask(newLayer,
new BoundingBoxDownloader(downloadArea), progressMonitor);
currentBounds = new Bounds(downloadArea);
// We need submit instead of execute so we can wait for it to finish and get the error
// message if necessary. If no one calls getErrorMessage() it just behaves like execute.
return Main.worker.submit(downloadTask);
}
/**
* Loads a given URL from the OSM Server
* @param True if the data should be saved to a new layer
* @param The URL as String
*/
public Future<?> loadUrl(boolean new_layer, String url, ProgressMonitor progressMonitor) {
downloadTask = new DownloadTask(new_layer,
new OsmServerLocationReader(url),
progressMonitor);
currentBounds = null;
return Main.worker.submit(downloadTask);
}
public void cancel() {
if (downloadTask != null) {
downloadTask.cancel();
}
}
private class DownloadTask extends PleaseWaitRunnable {
private OsmServerReader reader;
private DataSet dataSet;
private boolean newLayer;
public DownloadTask(boolean newLayer, OsmServerReader reader, ProgressMonitor progressMonitor) {
super(tr("Downloading data"), progressMonitor, false);
this.reader = reader;
this.newLayer = newLayer;
}
@Override public void realRun() throws IOException, SAXException, OsmTransferException {
try {
if (isCanceled())
return;
dataSet = reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
} catch(Exception e) {
if (isCanceled()) {
logger.warning(tr("Ignoring exception because download has been cancelled. Exception was: {0}", e.toString()));
return;
}
if (e instanceof OsmTransferCancelledException) {
setCanceled(true);
return;
} else if (e instanceof OsmTransferException) {
rememberException(e);
} else {
rememberException(new OsmTransferException(e));
}
DownloadOsmTask.this.setFailed(true);
}
}
protected OsmDataLayer getEditLayer() {
if (!Main.isDisplayingMapView()) return null;
return Main.map.mapView.getEditLayer();
}
protected int getNumDataLayers() {
int count = 0;
if (!Main.isDisplayingMapView()) return 0;
Collection<Layer> layers = Main.map.mapView.getAllLayers();
for (Layer layer : layers) {
if (layer instanceof OsmDataLayer) {
count++;
}
}
return count;
}
protected OsmDataLayer getFirstDataLayer() {
if (!Main.isDisplayingMapView()) return null;
Collection<Layer> layers = Main.map.mapView.getAllLayersAsList();
for (Layer layer : layers) {
if (layer instanceof OsmDataLayer)
return (OsmDataLayer) layer;
}
return null;
}
@Override protected void finish() {
if (isFailed() || isCanceled())
return;
if (dataSet == null)
return; // user canceled download or error occurred
if (dataSet.allPrimitives().isEmpty()) {
rememberErrorMessage(tr("No data found in this area."));
// need to synthesize a download bounds lest the visual indication of downloaded
// area doesn't work
dataSet.dataSources.add(new DataSource(currentBounds != null ? currentBounds : new Bounds(new LatLon(0, 0)), "OpenStreetMap server"));
}
rememberDownloadedData(dataSet);
int numDataLayers = getNumDataLayers();
if (newLayer || numDataLayers == 0 || (numDataLayers > 1 && getEditLayer() == null)) {
// the user explicitly wants a new layer, we don't have any layer at all
// or it is not clear which layer to merge to
//
OsmDataLayer layer = new OsmDataLayer(dataSet, OsmDataLayer.createNewName(), null);
final boolean isDisplayingMapView = Main.isDisplayingMapView();
Main.main.addLayer(layer);
// If the mapView is not there yet, we cannot calculate the bounds (see constructor of MapView).
// Otherwise jump to the current download.
if (isDisplayingMapView) {
BoundingXYVisitor v = new BoundingXYVisitor();
if (currentBounds != null) {
v.visit(currentBounds);
} else {
v.computeBoundingBox(dataSet.getNodes());
}
Main.map.mapView.recalculateCenterScale(v);
}
} else {
OsmDataLayer target;
target = getEditLayer();
if (target == null) {
target = getFirstDataLayer();
}
target.mergeFrom(dataSet);
BoundingXYVisitor v = new BoundingXYVisitor();
if (currentBounds != null) {
v.visit(currentBounds);
} else {
v.computeBoundingBox(dataSet.getNodes());
}
Main.map.mapView.recalculateCenterScale(v);
target.onPostDownloadFromServer();
}
}
@Override protected void cancel() {
setCanceled(true);
if (reader != null) {
reader.cancel();
}
}
}
}