root / trunk / libraries / libTopology / src / com / vividsolutions / jcs / conflate / roads / ConflationSession.java @ 22873
History | View | Annotate | Download (15.2 KB)
1 |
package com.vividsolutions.jcs.conflate.roads; |
---|---|
2 |
|
3 |
import java.io.*; |
4 |
import java.util.*; |
5 |
|
6 |
import com.vividsolutions.jcs.conflate.roads.match.RoadMatchOptions; |
7 |
import com.vividsolutions.jcs.conflate.roads.match.RoadMatcherProcess; |
8 |
import com.vividsolutions.jcs.conflate.roads.model.*; |
9 |
import com.vividsolutions.jcs.conflate.roads.model.sourcematchconsistency.SourceMatchConsistencyRule; |
10 |
import com.vividsolutions.jcs.jump.FUTURE_CollectionUtil; |
11 |
import com.vividsolutions.jts.geom.*; |
12 |
import com.vividsolutions.jts.util.Assert; |
13 |
import com.vividsolutions.jump.feature.*; |
14 |
import com.vividsolutions.jump.task.TaskMonitor; |
15 |
import com.vividsolutions.jump.util.*; |
16 |
import com.vividsolutions.jump.workbench.ui.plugin.AddNewLayerPlugIn; |
17 |
|
18 |
/**
|
19 |
* The overall container class that represents the entire process of merging two
|
20 |
* RoadNetworks.
|
21 |
*
|
22 |
* @author David Zwiers, Vivid Solutions Inc.
|
23 |
* @author jaquino Vivid Solutions Inc.
|
24 |
*/
|
25 |
public class ConflationSession implements Serializable { |
26 |
|
27 |
public static final String DEFAULT_NAME = "New Session"; |
28 |
|
29 |
/**
|
30 |
* Returns any coincident segments in the input data. If there are no
|
31 |
* coincident segments in a source dataset, the corresponding feature
|
32 |
* collection will be empty.
|
33 |
*
|
34 |
* @return an array of 2 {@link FeatureCollection}s containing any
|
35 |
* coincident segments.
|
36 |
*/
|
37 |
private static FeatureCollection[] getIllegalGeometries( |
38 |
FeatureCollection[] inputFC) {
|
39 |
FeatureCollection[] illegalGeometryFC = new FeatureCollection[2]; |
40 |
illegalGeometryFC[0] = getIllegalGeometries(inputFC[0]); |
41 |
illegalGeometryFC[1] = getIllegalGeometries(inputFC[1]); |
42 |
return illegalGeometryFC;
|
43 |
} |
44 |
|
45 |
private boolean automatedProcessRunning = false; |
46 |
|
47 |
/**
|
48 |
* Checks for valid edge geometry. Edge geometry must be simple LineStrings.
|
49 |
* If there are no illegal geometries the feature collection will be empty.
|
50 |
*
|
51 |
* @return a {@link FeatureCollection}containing any edges with illegal
|
52 |
* geometry
|
53 |
*/
|
54 |
private static FeatureCollection getIllegalGeometries( |
55 |
FeatureCollection inputFC) { |
56 |
FeatureCollection fc = new FeatureDataset(inputFC.getFeatureSchema());
|
57 |
for (Iterator i = inputFC.iterator(); i.hasNext();) { |
58 |
Feature f = (Feature) i.next(); |
59 |
Geometry geom = f.getGeometry(); |
60 |
boolean isValid = geom instanceof LineString && geom.isSimple(); |
61 |
if (!isValid)
|
62 |
fc.add(f); |
63 |
} |
64 |
return fc;
|
65 |
} |
66 |
|
67 |
/** Transient because the UI stuff is not Serializable */
|
68 |
private transient RoadsEventFirer roadsEventFirer; |
69 |
|
70 |
private boolean autoMatched = false; |
71 |
|
72 |
private String name; |
73 |
|
74 |
private Statistics statistics = new Statistics(this); |
75 |
|
76 |
private RoadMatchOptions matchOptions = new RoadMatchOptions(); |
77 |
|
78 |
private ConsistencyRule consistencyRule = new SourceMatchConsistencyRule(); |
79 |
|
80 |
private FeatureCollection[] originalFeatureCollections; |
81 |
|
82 |
private FeatureCollection[] coincidentSegmentFeatureCollections; |
83 |
|
84 |
private FeatureCollection[] illegalGeometryFC; |
85 |
|
86 |
private PrecedenceRuleEngine precedenceRuleEngine;
|
87 |
|
88 |
private boolean[] warningAboutAdjustments = new boolean[] { false, false }; |
89 |
|
90 |
private FeatureCollection[] contextFeatureCollections; |
91 |
|
92 |
private FeatureCollection[] unmatchedNodeConstraints; |
93 |
|
94 |
public boolean isWarningAboutAdjustments(int i) { |
95 |
//TODO: Combine #warningAboutAdjustments with
|
96 |
//RoadNetwork#editable into a single field: #adjustmentConstraint.
|
97 |
//Easiest is to implement it as a String -- if instead we go with the
|
98 |
//enum pattern, remember to implement #readResolve.
|
99 |
//[Jon Aquino 2004-04-30]
|
100 |
return warningAboutAdjustments[i];
|
101 |
} |
102 |
|
103 |
public void setWarningAboutAdjustments(int i, |
104 |
boolean warningAboutAdjustments) {
|
105 |
this.warningAboutAdjustments[i] = warningAboutAdjustments;
|
106 |
} |
107 |
|
108 |
/**
|
109 |
* This is not dead code! #readObject is a special serialization method.
|
110 |
*/
|
111 |
private void readObject(ObjectInputStream in) throws IOException, |
112 |
ClassNotFoundException {
|
113 |
in.defaultReadObject(); |
114 |
getRoadsEventFirer().addListener(statistics); |
115 |
} |
116 |
|
117 |
public ConflationSession(FeatureCollection originalFeatureCollection0,
|
118 |
FeatureCollection originalFeatureCollection1) { |
119 |
this(DEFAULT_NAME, originalFeatureCollection0,
|
120 |
originalFeatureCollection1, AddNewLayerPlugIn |
121 |
.createBlankFeatureCollection(), AddNewLayerPlugIn |
122 |
.createBlankFeatureCollection(), AddNewLayerPlugIn |
123 |
.createBlankFeatureCollection(), AddNewLayerPlugIn |
124 |
.createBlankFeatureCollection()); |
125 |
} |
126 |
|
127 |
public ConflationSession(String name, |
128 |
FeatureCollection originalFeatureCollection0, |
129 |
FeatureCollection originalFeatureCollection1, |
130 |
FeatureCollection contextFeatureCollection0, |
131 |
FeatureCollection contextFeatureCollection1, |
132 |
FeatureCollection nodeConstraints0, |
133 |
FeatureCollection nodeConstraints1) { |
134 |
setName(name); |
135 |
getRoadsEventFirer().addListener(statistics); |
136 |
originalFeatureCollections = new FeatureCollection[] { |
137 |
originalFeatureCollection0, originalFeatureCollection1 }; |
138 |
contextFeatureCollections = new FeatureCollection[] { |
139 |
contextFeatureCollection0, contextFeatureCollection1 }; |
140 |
// do validations on input features
|
141 |
illegalGeometryFC = getIllegalGeometries(originalFeatureCollections); |
142 |
FeatureCollection hideConflationAttributesFeatureCollection0 = new HideConflationAttributesFeatureCollectionWrapper(
|
143 |
originalFeatureCollection0); |
144 |
FeatureCollection hideConflationAttributesFeatureCollection1 = new HideConflationAttributesFeatureCollectionWrapper(
|
145 |
originalFeatureCollection1); |
146 |
sourceNetworks[0] = new RoadNetwork(SourceFeature |
147 |
.createSchema(hideConflationAttributesFeatureCollection0 |
148 |
.getFeatureSchema()), this);
|
149 |
sourceNetworks[1] = new RoadNetwork(SourceFeature |
150 |
.createSchema(hideConflationAttributesFeatureCollection1 |
151 |
.getFeatureSchema()), this);
|
152 |
//Ensure sourceNetworks field is populated before adding to the
|
153 |
//networks, because RoadNetwork#add leads to a call to
|
154 |
//ConflationSession#getSourceNetworkN [Jon Aquino 12/1/2003]
|
155 |
initializeSourceNetwork(sourceNetworks[0],
|
156 |
hideConflationAttributesFeatureCollection0); |
157 |
initializeSourceNetwork(sourceNetworks[1],
|
158 |
hideConflationAttributesFeatureCollection1); |
159 |
unmatchedNodeConstraints = new FeatureCollection[] { |
160 |
assignNodeConstraints(nodeConstraints0, getSourceNetwork(0)),
|
161 |
assignNodeConstraints(nodeConstraints1, getSourceNetwork(1)) };
|
162 |
} |
163 |
|
164 |
private static FeatureCollection assignNodeConstraints( |
165 |
FeatureCollection nodeConstraints, RoadNetwork network) { |
166 |
Collection unassignedNodeConstraints = new ArrayList(); |
167 |
Set nodeConstraintCoordinates = new HashSet(); |
168 |
for (Iterator i = nodeConstraints.iterator(); i.hasNext();) { |
169 |
Feature feature = (Feature) i.next(); |
170 |
//Allow multipoints as workaround for ShapefileWriter bug: it seems
|
171 |
//to save points as multipoints. [Jon Aquino 2004-06-01]
|
172 |
if (!(feature.getGeometry() instanceof Point) |
173 |
&& !(feature.getGeometry() instanceof MultiPoint)) {
|
174 |
unassignedNodeConstraints.add(feature.getGeometry()); |
175 |
continue;
|
176 |
} |
177 |
nodeConstraintCoordinates.addAll(Arrays.asList(feature
|
178 |
.getGeometry().getCoordinates())); |
179 |
} |
180 |
unassignedNodeConstraints.addAll(assignNodeConstraints( |
181 |
nodeConstraintCoordinates, network)); |
182 |
return toFeatureCollection(unassignedNodeConstraints);
|
183 |
} |
184 |
|
185 |
private static FeatureCollection toFeatureCollection(Collection geometries) { |
186 |
FeatureCollection featureCollection = AddNewLayerPlugIn |
187 |
.createBlankFeatureCollection(); |
188 |
for (Iterator i = geometries.iterator(); i.hasNext();) { |
189 |
Geometry geometry = (Geometry) i.next(); |
190 |
Feature feature = new BasicFeature(featureCollection
|
191 |
.getFeatureSchema()); |
192 |
feature.setGeometry(geometry); |
193 |
featureCollection.add(feature); |
194 |
} |
195 |
return featureCollection;
|
196 |
} |
197 |
|
198 |
private static Collection assignNodeConstraints( |
199 |
Set nodeConstraintCoordinates, RoadNetwork network) {
|
200 |
Set unassignedNodeConstraints = new HashSet(nodeConstraintCoordinates); |
201 |
for (Iterator i = network.getGraph().getEdges().iterator(); i.hasNext();) { |
202 |
SourceRoadSegment roadSegment = (SourceRoadSegment) i.next(); |
203 |
roadSegment.setStartNodeConstrained(nodeConstraintCoordinates |
204 |
.contains(roadSegment.getApparentStartCoordinate())); |
205 |
roadSegment.setEndNodeConstrained(nodeConstraintCoordinates |
206 |
.contains(roadSegment.getApparentEndCoordinate())); |
207 |
unassignedNodeConstraints.remove(roadSegment |
208 |
.getApparentStartCoordinate()); |
209 |
unassignedNodeConstraints.remove(roadSegment |
210 |
.getApparentEndCoordinate()); |
211 |
} |
212 |
return CollectionUtil.collect(unassignedNodeConstraints, new Block() { |
213 |
public Object yield(Object coordinate) { |
214 |
return geometryFactory.createPoint((Coordinate) coordinate);
|
215 |
} |
216 |
}); |
217 |
} |
218 |
|
219 |
private static GeometryFactory geometryFactory = new GeometryFactory(); |
220 |
|
221 |
/**
|
222 |
* Returns any coincident segments in the input data. If there are no
|
223 |
* coincident segments in a source dataset, the corresponding feature
|
224 |
* collection will be empty.
|
225 |
*
|
226 |
* @return an array of 2 {@link FeatureCollection}s containing any
|
227 |
* coincident segments.
|
228 |
*/
|
229 |
public FeatureCollection[] getCoincidentSegments() { |
230 |
if (coincidentSegmentFeatureCollections == null) { |
231 |
coincidentSegmentFeatureCollections = new FeatureCollection[2]; |
232 |
coincidentSegmentFeatureCollections[0] = sourceNetworks[0] |
233 |
.checkCoincidentEdges(); |
234 |
coincidentSegmentFeatureCollections[1] = sourceNetworks[1] |
235 |
.checkCoincidentEdges(); |
236 |
} |
237 |
return coincidentSegmentFeatureCollections;
|
238 |
} |
239 |
|
240 |
/**
|
241 |
* Returns any coincident segments in the input data. If there are no
|
242 |
* coincident segments in a source dataset, the corresponding feature
|
243 |
* collection will be empty.
|
244 |
*
|
245 |
* @return an array of 2 {@link FeatureCollection}s containing any
|
246 |
* coincident segments.
|
247 |
*/
|
248 |
public FeatureCollection[] getIllegalGeometries() { |
249 |
return illegalGeometryFC;
|
250 |
} |
251 |
|
252 |
public RoadMatchOptions getMatchOptions() {
|
253 |
return matchOptions;
|
254 |
} |
255 |
|
256 |
public RoadMatcherProcess autoMatch(final TaskMonitor monitor) { |
257 |
//Assert.isTrue(!autoMatched);
|
258 |
// MD - for now, run automatching right away. Probably eventually needs
|
259 |
// to be done under user control
|
260 |
final RoadMatcherProcess rm = new RoadMatcherProcess( |
261 |
getSourceNetwork(0), getSourceNetwork(1)); |
262 |
doAutomatedProcess(new Block() {
|
263 |
public Object yield() { |
264 |
rm.match(matchOptions, monitor); |
265 |
return null; |
266 |
} |
267 |
}); |
268 |
autoMatched = true;
|
269 |
return rm;
|
270 |
} |
271 |
|
272 |
public void updateResultStates(final TaskMonitor monitor) { |
273 |
Block block = new Block() {
|
274 |
public Object yield() { |
275 |
int totalRoadSegments = getSourceNetwork(0).getGraph() |
276 |
.getEdges().size() |
277 |
+ getSourceNetwork(1).getGraph().getEdges().size();
|
278 |
int[] roadSegmentsUpdated = new int[] { 0 }; |
279 |
updateResultStates(getSourceNetwork(0), monitor,
|
280 |
totalRoadSegments, roadSegmentsUpdated); |
281 |
updateResultStates(getSourceNetwork(1), monitor,
|
282 |
totalRoadSegments, roadSegmentsUpdated); |
283 |
return null; |
284 |
} |
285 |
}; |
286 |
if (getConsistencyRule() instanceof Optimizable) { |
287 |
((Optimizable) getConsistencyRule()).doOptimizedOp(block); |
288 |
} else {
|
289 |
block.yield(); |
290 |
} |
291 |
} |
292 |
|
293 |
public void doAutomatedProcess(Block process) { |
294 |
boolean automatedProcessRunningOriginally = automatedProcessRunning;
|
295 |
automatedProcessRunning = true;
|
296 |
try {
|
297 |
process.yield(); |
298 |
} finally {
|
299 |
automatedProcessRunning = automatedProcessRunningOriginally; |
300 |
} |
301 |
} |
302 |
|
303 |
private void updateResultStates(RoadNetwork network, TaskMonitor monitor, |
304 |
int totalRoadSegments, int[] roadSegmentsUpdated) { |
305 |
monitor.report("Updating result states");
|
306 |
for (Iterator i = network.getGraph().getEdges().iterator(); i.hasNext();) { |
307 |
SourceRoadSegment roadSegment = (SourceRoadSegment) i.next(); |
308 |
roadSegment.setResultState(ResultStateRules.instance() |
309 |
.determineResultState(roadSegment)); |
310 |
monitor.report(++roadSegmentsUpdated[0], totalRoadSegments,
|
311 |
"road segments");
|
312 |
} |
313 |
} |
314 |
|
315 |
private void initializeSourceNetwork(RoadNetwork sourceNetwork, |
316 |
FeatureCollection originalFeatureCollection) { |
317 |
for (Iterator i = originalFeatureCollection.iterator(); i.hasNext();) { |
318 |
Feature originalFeature = (Feature) i.next(); |
319 |
Geometry geom = (Geometry) originalFeature.getGeometry().clone(); |
320 |
if (geom instanceof LineString) { |
321 |
sourceNetwork.add(new SourceRoadSegment((LineString) geom,
|
322 |
originalFeature, sourceNetwork)); |
323 |
} |
324 |
} |
325 |
} |
326 |
|
327 |
public RoadNetwork getSourceNetwork(int index) { |
328 |
return sourceNetworks[index];
|
329 |
} |
330 |
|
331 |
public RoadNetwork getSourceNetwork(String name) { |
332 |
if (sourceNetworks[0].getName().equals(name)) { |
333 |
return sourceNetworks[0]; |
334 |
} |
335 |
if (sourceNetworks[1].getName().equals(name)) { |
336 |
return sourceNetworks[1]; |
337 |
} |
338 |
Assert.shouldNeverReachHere(); |
339 |
return null; |
340 |
} |
341 |
|
342 |
public FeatureCollection getOriginalFeatureCollection(int index) { |
343 |
return originalFeatureCollections[index];
|
344 |
} |
345 |
|
346 |
public FeatureCollection getContextFeatureCollection(int index) { |
347 |
return contextFeatureCollections[index];
|
348 |
} |
349 |
|
350 |
private RoadNetwork[] sourceNetworks = new RoadNetwork[2]; |
351 |
|
352 |
public int getIndex(RoadNetwork network){ |
353 |
if(sourceNetworks[0].equals(network)) |
354 |
return 0; |
355 |
if(sourceNetworks[1].equals(network)) |
356 |
return 1; |
357 |
return -1; |
358 |
} |
359 |
|
360 |
public Statistics getStatistics() {
|
361 |
return statistics;
|
362 |
} |
363 |
|
364 |
public ConsistencyRule getConsistencyRule() {
|
365 |
return consistencyRule;
|
366 |
} |
367 |
|
368 |
private Blackboard blackboard = new Blackboard(); |
369 |
|
370 |
private File file; |
371 |
|
372 |
public Blackboard getBlackboard() {
|
373 |
return blackboard;
|
374 |
} |
375 |
|
376 |
public void setFile(File file) { |
377 |
this.file = file;
|
378 |
} |
379 |
|
380 |
public File getFile() { |
381 |
return file;
|
382 |
} |
383 |
|
384 |
public ConflationSession setConsistencyRule(ConsistencyRule consistencyRule) {
|
385 |
this.consistencyRule = consistencyRule;
|
386 |
return this; |
387 |
} |
388 |
|
389 |
public boolean isAutoMatched() { |
390 |
return autoMatched;
|
391 |
} |
392 |
|
393 |
public RoadsEventFirer getRoadsEventFirer() {
|
394 |
if (roadsEventFirer == null) { |
395 |
//Get here after deserialization [Jon Aquino 2004-01-29]
|
396 |
roadsEventFirer = new RoadsEventFirer();
|
397 |
} |
398 |
return roadsEventFirer;
|
399 |
} |
400 |
|
401 |
public void setMatchOptions(RoadMatchOptions matchOptions) { |
402 |
this.matchOptions = matchOptions;
|
403 |
} |
404 |
|
405 |
public PrecedenceRuleEngine getPrecedenceRuleEngine() {
|
406 |
return precedenceRuleEngine;
|
407 |
} |
408 |
|
409 |
public void setPrecedenceRuleEngine( |
410 |
PrecedenceRuleEngine precedenceRuleEngine) { |
411 |
this.precedenceRuleEngine = precedenceRuleEngine;
|
412 |
} |
413 |
|
414 |
private boolean locked = false; |
415 |
|
416 |
public String getName() { |
417 |
return name;
|
418 |
} |
419 |
|
420 |
public void setName(String name) { |
421 |
this.name = name;
|
422 |
} |
423 |
|
424 |
public Collection getRoadSegments() { |
425 |
return FUTURE_CollectionUtil.concatenate(getSourceNetwork(0).getGraph() |
426 |
.getEdges(), getSourceNetwork(1).getGraph().getEdges());
|
427 |
} |
428 |
|
429 |
public boolean isAutomatedProcessRunning() { |
430 |
return automatedProcessRunning;
|
431 |
} |
432 |
|
433 |
public FeatureCollection[] getUnmatchedNodeConstraints() { |
434 |
return unmatchedNodeConstraints;
|
435 |
} |
436 |
|
437 |
/**
|
438 |
* @param locked
|
439 |
*/
|
440 |
public void setLocked(boolean locked) { |
441 |
this.locked = locked;
|
442 |
} |
443 |
|
444 |
/**
|
445 |
* @return locked
|
446 |
*/
|
447 |
public boolean isLocked() { |
448 |
return locked;
|
449 |
} |
450 |
|
451 |
private boolean filterDataSetsZoom = true; |
452 |
|
453 |
/**
|
454 |
* @return Returns the filterDataSetsZoom.
|
455 |
*/
|
456 |
public boolean isFilterDataSetsZoom() { |
457 |
return filterDataSetsZoom;
|
458 |
} |
459 |
|
460 |
/**
|
461 |
* @param filterDataSetsZoom The filterDataSetsZoom to set.
|
462 |
*/
|
463 |
public void setFilterDataSetsZoom(boolean filterDataSetsZoom) { |
464 |
this.filterDataSetsZoom = filterDataSetsZoom;
|
465 |
} |
466 |
|
467 |
private HashMap dataLayers = new HashMap(); |
468 |
|
469 |
public void addDataLayer(String string, FeatureCollection collection) { |
470 |
dataLayers.put(string, collection); |
471 |
} |
472 |
|
473 |
public FeatureCollection getDataLayer(String string){ |
474 |
return (FeatureCollection) dataLayers.get(string);
|
475 |
} |
476 |
|
477 |
public List getDataLayerNames(){ |
478 |
return new ArrayList(dataLayers.keySet()); |
479 |
} |
480 |
} |