Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#45) Implemented realtime bounding box calculation. #46

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions src/main/java/org/geojson/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class Feature extends GeoJsonObject {
@JsonInclude(JsonInclude.Include.ALWAYS)
private Map<String, Object> properties = new HashMap<String, Object>();
@JsonInclude(JsonInclude.Include.ALWAYS)
private GeoJsonObject geometry;
private GeoJsonObject geometry = null ;
private String id;

public void setProperty(String key, Object value) {
Expand All @@ -26,16 +26,24 @@ public Map<String, Object> getProperties() {
return properties;
}

public void setProperties(Map<String, Object> properties) {
this.properties = properties;
public void setProperties( Map<String,Object> properties )
{
if( properties == null ) this.properties.clear() ;
else this.properties = properties ;
}

public GeoJsonObject getGeometry() {
return geometry;
}

public void setGeometry(GeoJsonObject geometry) {
this.geometry = geometry;
public GeoJsonObject getGeometry()
{ return geometry ; }

/**
* Sets the geometry of the feature.
* Implies recalculation of the feature's bounding box.
* @param geometry the new feature geometry
*/
public void setGeometry( GeoJsonObject geometry )
{
this.geometry = geometry ;
this.calculateBounds() ;
}

public String getId() {
Expand All @@ -46,6 +54,14 @@ public void setId(String id) {
this.id = id;
}

@Override
public double[] calculateBounds()
{
if( this.geometry == null ) this.setBbox(null) ;
else this.setBbox( this.getGeometry().getBbox() ) ;
return this.getBbox() ;
}

@Override
public <T> T accept(GeoJsonObjectVisitor<T> geoJsonObjectVisitor) {
return geoJsonObjectVisitor.visit(this);
Expand Down
60 changes: 49 additions & 11 deletions src/main/java/org/geojson/FeatureCollection.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package org.geojson;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.*;

public class FeatureCollection extends GeoJsonObject implements Iterable<Feature> {

Expand All @@ -13,17 +10,58 @@ public List<Feature> getFeatures() {
return features;
}

public void setFeatures(List<Feature> features) {
this.features = features;
/**
* Sets a new list of features for the collection.
* Implies a recalculation of the bounding box around the collection.
* @param features the new list of features
*/
public void setFeatures( List<Feature> features )
{
if( features == null ) this.features.clear() ;
else this.features = features ;
this.calculateBounds() ;
}

public FeatureCollection add(Feature feature) {
features.add(feature);
return this;
@Override
public double[] calculateBounds()
{
if( this.features.isEmpty() )
this.setBbox(null) ;
else
{
double[] box = STARTING_BOUNDS.clone() ;
for( Feature f : this.getFeatures() )
accumulateBounds( box, f.getBbox() ) ;
this.setBbox(box) ;
}
return this.getBbox() ;
}

/**
* Adds a feature to the collection.
* Implies a recalculation of the bounding box around the collection.
* @param feature the feature to be added
* @return (fluid)
*/
public FeatureCollection add( Feature feature )
{
if( feature == null ) return this ; // trivially
features.add(feature) ;
this.setBbox( accumulateBounds(
this.getBbox(), feature.calculateBounds() )) ;
return this ;
}

public void addAll(Collection<Feature> features) {
this.features.addAll(features);
/**
* Adds a collection of features to this collection.
* Implies a recalculation of the bounding box around the collection.
* @param features the features to be added
*/
public void addAll( Collection<Feature> features )
{
if( features == null || features.isEmpty() ) return ; // trivially
this.features.addAll(features) ;
this.calculateBounds() ;
}

@Override
Expand Down
97 changes: 88 additions & 9 deletions src/main/java/org/geojson/GeoJsonObject.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.geojson;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonSubTypes;
Expand All @@ -9,16 +10,83 @@

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

@JsonTypeInfo(property = "type", use = Id.NAME)
@JsonSubTypes({ @Type(Feature.class), @Type(Polygon.class), @Type(MultiPolygon.class), @Type(FeatureCollection.class),
@Type(Point.class), @Type(MultiPoint.class), @Type(MultiLineString.class), @Type(LineString.class),
@Type(GeometryCollection.class) })
@JsonInclude(Include.NON_NULL)
public abstract class GeoJsonObject implements Serializable {

public abstract class GeoJsonObject implements Serializable
{
private Crs crs;
private double[] bbox;

/**
* A bounding box around the object, as currently configured.
* This is initialized as {@code null} and dynamically updated each time the
* object's geometry changes.
*/
private double[] bbox = null ;

/**
* Starting bounds to guarantee that the various {@code calculateBounds}
* methods will work out correctly.
* @since issue #45
*/
protected static final double[] STARTING_BOUNDS =
{
Double.MAX_VALUE, Double.MAX_VALUE,
Double.MIN_VALUE, Double.MIN_VALUE
} ;

/**
* Calculates the bounding box around a list of points.
* @param points a list of points that compose a polygon
* @return a bounding box
* @since issue #45
*/
public static double[] calculateBounds( List<LngLatAlt> points )
{
double[] box = STARTING_BOUNDS.clone() ;
for( LngLatAlt point : points )
{
double longitude = point.getLongitude() ;
double latitude = point.getLatitude() ;
if( Double.compare( longitude, box[0] ) < 0 )
box[0] = longitude ;
if( Double.compare( latitude, box[1] ) < 0 )
box[1] = latitude ;
if( Double.compare( longitude, box[2] ) > 0 )
box[2] = longitude ;
if( Double.compare( latitude, box[3] ) > 0 )
box[3] = latitude ;
}
return box ;
}

/**
* Given a "current" bounding box, recalculates that box to account for an
* additional data point.
* @param currentBox the current bounding box
* @param newData a new data point
* @return the updated bounding box
* @since issue #45
*/
@SuppressWarnings("UnusedReturnValue")
public static double[] accumulateBounds( double[] currentBox, double[] newData )
{
if( newData == null ) return currentBox ; // nothing to add
if( currentBox == null ) currentBox = STARTING_BOUNDS.clone() ;
if( Double.compare( newData[0], currentBox[0] ) < 0 )
currentBox[0] = newData[0] ;
if( Double.compare( newData[1], currentBox[1] ) < 0 )
currentBox[1] = newData[1] ;
if( Double.compare( newData[2], currentBox[2] ) > 0 )
currentBox[2] = newData[2] ;
if( Double.compare( newData[3], currentBox[3] ) > 0 )
currentBox[3] = newData[3] ;
return currentBox ;
}

public Crs getCrs() {
return crs;
Expand All @@ -28,14 +96,25 @@ public void setCrs(Crs crs) {
this.crs = crs;
}

public double[] getBbox() {
return bbox;
}
@JsonIgnore
public double[] getBbox()
{ return bbox ; }

public void setBbox(double[] bbox) {
this.bbox = bbox;
}
/**
* Sets an explicit bounding box.
* @param bbox the explicit bounding box
*/
@JsonIgnore
protected void setBbox( double[] bbox )
{ this.bbox = bbox ; }

/**
* Calculates a bounding box around the object, and writes its coordinates
* back to the internal bounding box.
* @return the new bounding box (as from {@link #getBbox}.
* @since issue #45
*/
public abstract double[] calculateBounds() ;

public abstract <T> T accept(GeoJsonObjectVisitor<T> geoJsonObjectVisitor);

Expand Down
51 changes: 35 additions & 16 deletions src/main/java/org/geojson/Geometry.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,51 @@
package org.geojson;

import java.util.ArrayList;
import java.util.List;
import java.util.*;

public abstract class Geometry<T> extends GeoJsonObject {

protected List<T> coordinates = new ArrayList<T>();

public Geometry() {
}
public Geometry() { }

public Geometry(T... elements) {
for (T coordinate : elements) {
coordinates.add(coordinate);
}
}
public Geometry( List<T> coords )
{ this.setCoordinates(coords) ; }

public Geometry<T> add(T elements) {
coordinates.add(elements);
public Geometry( T... elements )
{ this.setCoordinates( Arrays.asList(elements) ) ; }

/**
* Adds some set of coordinates to the geometry.
* Implies recalculation of the feature's bounding box.
* @param elements the elements to be added
* @return (fluid)
*/
public Geometry<T> add( T elements )
{
if( elements == null ) return this ; // trivially
coordinates.add(elements) ;
if( GeoJsonObject.class.isAssignableFrom( elements.getClass() ) )
{ // Recalculate more cheaply by simply adding this element.
this.setBbox( accumulateBounds( this.getBbox(),
((GeoJsonObject)(elements)).getBbox() )) ;
}
else this.calculateBounds() ;
return this;
}

public List<T> getCoordinates() {
return coordinates;
}
public List<T> getCoordinates()
{ return coordinates ; }

public void setCoordinates(List<T> coordinates) {
this.coordinates = coordinates;
/**
* Sets the list of coordinates that make up the geometry.
* Implies a recalculation of the bounding box around the geometry.
* @param coordinates the new list of coordinates
*/
public void setCoordinates( List<T> coordinates )
{
if( coordinates == null ) this.coordinates.clear() ;
else this.coordinates = coordinates ;
this.calculateBounds() ;
}

@SuppressWarnings("rawtypes")
Expand Down
42 changes: 37 additions & 5 deletions src/main/java/org/geojson/GeometryCollection.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,54 @@ public class GeometryCollection extends GeoJsonObject implements Iterable<GeoJso

private List<GeoJsonObject> geometries = new ArrayList<GeoJsonObject>();

public GeometryCollection() {}

/**
* Constructs the collection with an initial list of geometries.
* @param geos the initial list of geometries to be added
* @since issue #45
*/
public GeometryCollection( List<GeoJsonObject> geos )
{ this.setGeometries(geos) ; }

public List<GeoJsonObject> getGeometries() {
return geometries;
}

public void setGeometries(List<GeoJsonObject> geometries) {
this.geometries = geometries;
public void setGeometries( List<GeoJsonObject> geometries )
{
if( geometries == null ) this.geometries.clear() ;
else this.geometries = geometries ;
this.calculateBounds() ;
}

@Override
public Iterator<GeoJsonObject> iterator() {
return geometries.iterator();
}

public GeometryCollection add(GeoJsonObject geometry) {
geometries.add(geometry);
return this;
public GeometryCollection add( GeoJsonObject geometry )
{
if( geometry == null ) return this ; // trivially
geometries.add(geometry) ;
this.setBbox( accumulateBounds(
this.getBbox(), geometry.calculateBounds() ) ) ;
return this ;
}

@Override
public double[] calculateBounds()
{
if( geometries.isEmpty() )
this.setBbox(null) ;
else
{
double[] box = STARTING_BOUNDS.clone() ;
for( GeoJsonObject geo : this.getGeometries() )
GeoJsonObject.accumulateBounds( box, geo.getBbox() ) ;
this.setBbox(box) ;
}
return this.getBbox() ;
}

@Override
Expand Down
Loading