source: josm/trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java@ 8240

Last change on this file since 8240 was 8240, checked in by simon04, 9 years ago

Open Location: enable loading of nodes/ways/relations/changesets from OSM website URL

Up to now, only corresponding API URLs have been supported.

  • Property svn:eol-style set to native
File size: 9.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions.downloadtasks;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.Date;
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.List;
10import java.util.Map;
11import java.util.concurrent.Future;
12import java.util.regex.Matcher;
13import java.util.regex.Pattern;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.Bounds;
17import org.openstreetmap.josm.data.osm.DataSet;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.NodeData;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
22import org.openstreetmap.josm.data.osm.PrimitiveData;
23import org.openstreetmap.josm.data.osm.PrimitiveId;
24import org.openstreetmap.josm.data.osm.RelationData;
25import org.openstreetmap.josm.data.osm.RelationMemberData;
26import org.openstreetmap.josm.data.osm.WayData;
27import org.openstreetmap.josm.data.osm.history.History;
28import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
29import org.openstreetmap.josm.data.osm.history.HistoryDataSetListener;
30import org.openstreetmap.josm.data.osm.history.HistoryNode;
31import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
32import org.openstreetmap.josm.data.osm.history.HistoryRelation;
33import org.openstreetmap.josm.data.osm.history.HistoryWay;
34import org.openstreetmap.josm.gui.history.HistoryLoadTask;
35import org.openstreetmap.josm.gui.progress.ProgressMonitor;
36import org.openstreetmap.josm.io.OsmApi;
37import org.openstreetmap.josm.io.OsmServerLocationReader;
38import org.openstreetmap.josm.io.OsmServerReader;
39import org.openstreetmap.josm.io.OsmTransferException;
40
41/**
42 * Task allowing to download OsmChange data (http://wiki.openstreetmap.org/wiki/OsmChange).
43 * @since 4530
44 */
45public class DownloadOsmChangeTask extends DownloadOsmTask {
46
47 private static final String OSM_WEBSITE_PATTERN = "https?://www\\.(osm|openstreetmap)\\.org/changeset/(\\p{Digit}+).*";
48
49 @Override
50 public String[] getPatterns() {
51 return new String[]{"https?://.*/api/0.6/changeset/\\p{Digit}+/download", // OSM API 0.6 changesets
52 OSM_WEBSITE_PATTERN, // OSM changesets
53 "https?://.*/.*\\.osc" // Remote .osc files
54 };
55 }
56
57 @Override
58 public String getTitle() {
59 return tr("Download OSM Change");
60 }
61
62 @Override
63 public Future<?> download(boolean newLayer, Bounds downloadArea,
64 ProgressMonitor progressMonitor) {
65 return null;
66 }
67
68 @Override
69 public Future<?> loadUrl(boolean new_layer, String url, ProgressMonitor progressMonitor) {
70 final Matcher matcher = Pattern.compile(OSM_WEBSITE_PATTERN).matcher(url);
71 if (matcher.matches()) {
72 url = OsmApi.getOsmApi().getBaseUrl() + "changeset/" + Long.parseLong(matcher.group(2)) + "/download";
73 }
74 downloadTask = new DownloadTask(new_layer, new OsmServerLocationReader(url), progressMonitor);
75 // Extract .osc filename from URL to set the new layer name
76 extractOsmFilename("https?://.*/(.*\\.osc)", url);
77 return Main.worker.submit(downloadTask);
78 }
79
80 protected class DownloadTask extends DownloadOsmTask.DownloadTask {
81
82 public DownloadTask(boolean newLayer, OsmServerReader reader,
83 ProgressMonitor progressMonitor) {
84 super(newLayer, reader, progressMonitor);
85 }
86
87 @Override
88 protected DataSet parseDataSet() throws OsmTransferException {
89 return reader.parseOsmChange(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
90 }
91
92 @Override
93 protected void finish() {
94 super.finish();
95 if (isFailed() || isCanceled() || downloadedData == null)
96 return; // user canceled download or error occurred
97 try {
98 // A changeset does not contain all referred primitives, this is the map of incomplete ones
99 // For each incomplete primitive, we'll have to get its state at date it was referred
100 Map<OsmPrimitive, Date> toLoad = new HashMap<>();
101 for (OsmPrimitive p : downloadedData.allNonDeletedPrimitives()) {
102 if (p.isIncomplete()) {
103 Date timestamp = null;
104 for (OsmPrimitive ref : p.getReferrers()) {
105 if (!ref.isTimestampEmpty()) {
106 timestamp = ref.getTimestamp();
107 break;
108 }
109 }
110 toLoad.put(p, timestamp);
111 }
112 }
113 if (isCanceled()) return;
114 // Let's load all required history
115 Main.worker.submit(new HistoryLoaderAndListener(toLoad));
116 } catch (Exception e) {
117 rememberException(e);
118 setFailed(true);
119 }
120 }
121 }
122
123 /**
124 * Loads history and updates incomplete primitives.
125 */
126 private static class HistoryLoaderAndListener extends HistoryLoadTask implements HistoryDataSetListener {
127
128 private final Map<OsmPrimitive, Date> toLoad;
129
130 public HistoryLoaderAndListener(Map<OsmPrimitive, Date> toLoad) {
131 this.toLoad = toLoad;
132 add(toLoad.keySet());
133 // Updating process is done after all history requests have been made
134 HistoryDataSet.getInstance().addHistoryDataSetListener(this);
135 }
136
137 @Override
138 public void historyUpdated(HistoryDataSet source, PrimitiveId id) {
139 Map<OsmPrimitive, Date> toLoadNext = new HashMap<>();
140 for (Iterator<OsmPrimitive> it = toLoad.keySet().iterator(); it.hasNext();) {
141 OsmPrimitive p = it.next();
142 History history = source.getHistory(p.getPrimitiveId());
143 Date date = toLoad.get(p);
144 // If the history has been loaded and a timestamp is known
145 if (history != null && date != null) {
146 // Lookup for the primitive version at the specified timestamp
147 HistoryOsmPrimitive hp = history.getByDate(date);
148 if (hp != null) {
149 PrimitiveData data = null;
150
151 switch (p.getType()) {
152 case NODE:
153 data = new NodeData();
154 ((NodeData)data).setCoor(((HistoryNode)hp).getCoords());
155 break;
156 case WAY:
157 data = new WayData();
158 List<Long> nodeIds = ((HistoryWay)hp).getNodes();
159 ((WayData)data).setNodes(nodeIds);
160 // Find incomplete nodes to load at next run
161 for (Long nodeId : nodeIds) {
162 if (p.getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE) == null) {
163 Node n = new Node(nodeId);
164 p.getDataSet().addPrimitive(n);
165 toLoadNext.put(n, date);
166 }
167 }
168 break;
169 case RELATION:
170 data = new RelationData();
171 List<RelationMemberData> members = ((HistoryRelation)hp).getMembers();
172 ((RelationData)data).setMembers(members);
173 break;
174 default: throw new AssertionError("Unknown primitive type");
175 }
176
177 data.setUser(hp.getUser());
178 try {
179 data.setVisible(hp.isVisible());
180 } catch (IllegalStateException e) {
181 Main.error("Cannot change visibility for "+p+": "+e.getMessage());
182 }
183 data.setTimestamp(hp.getTimestamp());
184 data.setKeys(hp.getTags());
185 data.setOsmId(hp.getId(), (int) hp.getVersion());
186
187 // Load the history data
188 try {
189 p.load(data);
190 // Forget this primitive
191 it.remove();
192 } catch (AssertionError e) {
193 Main.error("Cannot load "+p + ": " + e.getMessage());
194 }
195 }
196 }
197 }
198 source.removeHistoryDataSetListener(this);
199 if (toLoadNext.isEmpty()) {
200 // No more primitive to update. Processing is finished
201 // Be sure all updated primitives are correctly drawn
202 Main.map.repaint();
203 } else {
204 // Some primitives still need to be loaded
205 // Let's load all required history
206 Main.worker.submit(new HistoryLoaderAndListener(toLoadNext));
207 }
208 }
209
210 @Override
211 public void historyDataSetCleared(HistoryDataSet source) {
212 }
213 }
214}
Note: See TracBrowser for help on using the repository browser.