// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.core.bound.v0_6; import java.util.Map; import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer; import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer; import org.openstreetmap.osmosis.core.container.v0_6.EntityProcessor; import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer; import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer; import org.openstreetmap.osmosis.core.container.v0_6.WayContainer; import org.openstreetmap.osmosis.core.domain.v0_6.Bound; import org.openstreetmap.osmosis.core.domain.v0_6.Node; import org.openstreetmap.osmosis.core.lifecycle.ReleasableIterator; import org.openstreetmap.osmosis.core.store.GenericObjectSerializationFactory; import org.openstreetmap.osmosis.core.store.SimpleObjectStore; import org.openstreetmap.osmosis.core.task.v0_6.Sink; import org.openstreetmap.osmosis.core.task.v0_6.SinkSource; /** * Computes the minimal bounding box of an entity stream. * * A bound entity is emitted iff there are nodes on the input stream. * * Upstream bound entities are never passed through. If there is a bound entity * on the input stream, it is overwritten if there are any nodes or removed if * there are no nodes on the input stream. * * This implementation caches all objects of the input stream in a simple object * store. * * @author Igor Podolskiy */ public class BoundComputer implements SinkSource, EntityProcessor { private Sink sink; private SimpleObjectStore<EntityContainer> objects; private double top; private double bottom; private double left; private double right; private boolean nodesSeen; private String origin; /** * Creates a new bounding box computer instance. * * @param origin * The origin for the bound to set. */ public BoundComputer(String origin) { objects = new SimpleObjectStore<EntityContainer>(new GenericObjectSerializationFactory(), "cbbo", true); bottom = 0; top = 0; left = 0; right = 0; nodesSeen = false; this.origin = origin; } @Override public void initialize(Map<String, Object> metaTags) { sink.initialize(metaTags); } @Override public void process(EntityContainer entityContainer) { entityContainer.process(this); } @Override public void complete() { objects.complete(); if (nodesSeen) { sink.process(new BoundContainer(new Bound(right, left, top, bottom, this.origin))); } try (ReleasableIterator<EntityContainer> i = objects.iterate()) { while (i.hasNext()) { sink.process(i.next()); } } sink.complete(); } @Override public void close() { sink.close(); objects.close(); } @Override public void setSink(Sink sink) { this.sink = sink; } @Override public void process(BoundContainer bound) { // Do nothing, we'll generate a new bound later on } @Override public void process(NodeContainer nodeContainer) { Node node = nodeContainer.getEntity(); if (nodesSeen) { left = Math.min(left, node.getLongitude()); right = Math.max(right, node.getLongitude()); bottom = Math.min(bottom, node.getLatitude()); top = Math.max(top, node.getLatitude()); } else { left = node.getLongitude(); right = node.getLongitude(); top = node.getLatitude(); bottom = node.getLatitude(); nodesSeen = true; } objects.add(nodeContainer); } @Override public void process(WayContainer way) { objects.add(way); } @Override public void process(RelationContainer relation) { objects.add(relation); } }