root / trunk / libraries / libTopology / src / com / vividsolutions / jcs / conflate / roads / ConflationSession.java @ 22873
History | View | Annotate | Download (15.2 KB)
1 | 22873 | azabala | 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 | } |