/**
* Copyright 2013 OpenSocial Foundation
* Copyright 2013 International Business Machines Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Utility library for working with Activity Streams Actions
* Requires underscorejs.
*
* @author James M Snell (jasnell@us.ibm.com)
*/
package com.ibm.common.geojson;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Iterator;
import com.google.common.base.Objects;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
/**
* A GeoJSON Bounding Box (see http://geojson.org/geojson-spec.html#bounding-boxes)
* @author james
*/
public final class BoundingBox
implements Iterable<Float>, Serializable {
public static final class Builder
implements Supplier<BoundingBox> {
private final ImmutableList.Builder<Float> bounds =
ImmutableList.builder();
public Builder add(float value) {
this.bounds.add(value);
return this;
}
public Builder add(float... values) {
if (values != null)
for (float v : values)
this.bounds.add(v);
return this;
}
@Override
public BoundingBox get() {
return new BoundingBox(this);
}
}
private final ImmutableList<Float> bounds;
BoundingBox(Builder builder) {
this.bounds = builder.bounds.build();
}
@Override
public Iterator<Float> iterator() {
return bounds.iterator();
}
public String toString() {
return Objects.toStringHelper(BoundingBox.class)
.addValue(bounds)
.toString();
}
private static BoundingBox buildBoundingBox(
ImmutableSortedSet<Float> xs,
ImmutableSortedSet<Float> ys,
ImmutableSortedSet<Float> zs) {
BoundingBox.Builder bbox =
new BoundingBox.Builder()
.add(xs.first())
.add(ys.first());
if (!zs.isEmpty())
bbox.add(zs.first());
bbox.add(xs.last());
bbox.add(ys.last());
if (!zs.isEmpty())
bbox.add(zs.last());
return bbox.get();
}
protected static BoundingBox calculateBoundingBoxLineStrings(Iterable<LineString> lineStrings) {
ImmutableSortedSet.Builder<Float> xset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> yset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> zset =
ImmutableSortedSet.naturalOrder();
for (LineString ls : lineStrings) {
for (Position p : ls) {
xset.add(p.northing());
yset.add(p.easting());
if (p.hasAltitude())
zset.add(p.altitude());
}
}
return buildBoundingBox(
xset.build(),
yset.build(),
zset.build());
}
/**
* Calculate the Bounding Box for a collection of Polygon objects
* @param polygons Iterable<Polygon>
* @return BoundingBox
*/
public static BoundingBox calculateBoundingBoxPolygons(Iterable<Polygon> polygons) {
ImmutableSortedSet.Builder<Float> xset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> yset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> zset =
ImmutableSortedSet.naturalOrder();
for (Polygon polygon : polygons) {
for (LineString line : polygon) {
for (Position pos : line) {
xset.add(pos.northing());
yset.add(pos.easting());
if (pos.hasAltitude())
zset.add(pos.altitude());
}
}
}
return buildBoundingBox(
xset.build(),
yset.build(),
zset.build());
}
protected static BoundingBox calculateBoundingBoxPositions(Iterable<Position> positions) {
ImmutableSortedSet.Builder<Float> xset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> yset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> zset =
ImmutableSortedSet.naturalOrder();
for (Position pos : positions) {
xset.add(pos.northing());
yset.add(pos.easting());
if (pos.hasAltitude())
zset.add(pos.altitude());
}
return buildBoundingBox(
xset.build(),
yset.build(),
zset.build());
}
protected static BoundingBox calculateBoundingBox(Position position) {
BoundingBox.Builder bbox =
new BoundingBox.Builder();
bbox.add(position.northing());
bbox.add(position.easting());
if (position.hasAltitude())
bbox.add(position.altitude());
return bbox.get();
}
private static void addValues(
ImmutableSortedSet.Builder<Float> xset,
ImmutableSortedSet.Builder<Float> yset,
ImmutableSortedSet.Builder<Float> zset,
Iterable<Position> positions) {
for (Position position : positions) {
xset.add(position.northing());
yset.add(position.easting());
if (position.hasAltitude())
zset.add(position.altitude());
}
}
private static void addValuesLineString(
ImmutableSortedSet.Builder<Float> xset,
ImmutableSortedSet.Builder<Float> yset,
ImmutableSortedSet.Builder<Float> zset,
Iterable<LineString> lines) {
for (LineString ls : lines) {
addValues(xset,yset,zset,ls);
}
}
private static void addValuesPolygon(
ImmutableSortedSet.Builder<Float> xset,
ImmutableSortedSet.Builder<Float> yset,
ImmutableSortedSet.Builder<Float> zset,
Iterable<Polygon> polygons) {
for (Polygon poly : polygons) {
addValuesLineString(xset,yset,zset,poly);
}
}
protected static BoundingBox calculateBoundingBox(Geometry<?,?> geometry) {
return calculateBoundingBoxGeometries(
ImmutableList.<Geometry<?,?>>of(geometry));
}
@SuppressWarnings("unchecked")
protected static BoundingBox calculateBoundingBoxGeometries(
Iterable<Geometry<?,?>> geometries) {
ImmutableSortedSet.Builder<Float> xset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> yset =
ImmutableSortedSet.naturalOrder();
ImmutableSortedSet.Builder<Float> zset =
ImmutableSortedSet.naturalOrder();
for (Geometry<?,?> geo : geometries) {
switch(geo.type()) {
case POINT:
Point point = (Point) geo;
Position position = Iterables.getFirst(point.coordinates(),null);
xset.add(position.northing());
yset.add(position.easting());
if (position.hasAltitude())
zset.add(position.altitude());
break;
case LINESTRING:
case MULTIPOINT:
addValues(xset,yset,zset,(Iterable<Position>)geo);
break;
case MULTILINESTRING:
case POLYGON:
addValuesLineString(xset,yset,zset,(Iterable<LineString>)geo);
break;
case MULTIPOLYGON:
addValuesPolygon(xset,yset,zset,(Iterable<Polygon>)geo);
break;
default:
break;
}
}
return buildBoundingBox(
xset.build(),
yset.build(),
zset.build());
}
protected static BoundingBox calculateBoundingBoxFeatures(Iterable<Feature> features) {
ImmutableList.Builder<Geometry<?,?>> list =
ImmutableList.builder();
for (Feature feature : features) {
Geometry<?,?> geometry = feature.geometry();
if (geometry != null)
list.add(geometry);
}
return calculateBoundingBoxGeometries(list.build());
}
Object writeReplace() throws java.io.ObjectStreamException {
return new SerializedForm(this);
}
private static class SerializedForm implements Serializable {
private static final long serialVersionUID = -2060301713159936285L;
private ImmutableList<Float> bounds;
protected SerializedForm(BoundingBox obj) {
this.bounds = obj.bounds;
}
Object readResolve() throws ObjectStreamException {
BoundingBox.Builder builder =
new BoundingBox.Builder();
builder.bounds.addAll(bounds);
return builder.get();
}
}
}