/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.core.geometry;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import com.opendoorlogistics.api.geometry.LatLong;
import com.opendoorlogistics.api.geometry.LatLongToScreen;
import com.opendoorlogistics.api.geometry.ODLGeom;
import com.opendoorlogistics.api.geometry.ODLGeom.GeomType;
import com.opendoorlogistics.core.gis.map.OnscreenGeometry;
import com.opendoorlogistics.core.gis.map.data.LatLongImpl;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* Immutable geometry class. An ODLGeom may not be modified after creation.
* Internally this class uses the JTS geometry class.
* @author Phil
*
*/
public abstract class ODLGeomImpl implements ODLGeom{
private ODLGeom [] children;
public abstract String toText();
@Override
public String toString(){
return toText();
}
//public abstract void putInCache(Object cacheKey, Object data);
//public abstract Object getFromCache(Object cacheKey);
public abstract Geometry getJTSGeometry();
/**
* For linked geometry, return true if the geometry has been
* loaded OR been attempted to be loaded.
* @return
*/
public abstract boolean isLoaded();
static{
Spatial.initSpatial();
}
@Override
public abstract int getPointsCount() ;
public abstract long getEstimatedSizeInBytes();
public abstract Point2D getWorldBitmapCentroid(LatLongToScreen latLongToScreen);
public abstract LatLong getWGSCentroid() ;
public abstract Envelope getWGSBounds();
public abstract Rectangle2D getWorldBitmapBounds(LatLongToScreen latLongToScreen);
public abstract boolean isLineString();
public abstract OnscreenGeometry createOnscreenGeometry(LatLongToScreen converter) ;
public enum AtomicGeomType{
POINT,
LINESTRING,
POLYGON
}
public abstract int getAtomicGeomCount(AtomicGeomType type);
public int getNbChildGeometries(){
Geometry jts = getJTSGeometry();
return jts!=null ? jts.getNumGeometries():0;
}
public synchronized ODLGeom getChildGeom(int i){
Geometry jts = getJTSGeometry();
if(!GeometryCollection.class.isInstance(jts)){
if(i>0){
throw new RuntimeException();
}
return this;
}
int n = getNbChildGeometries();
if(children==null){
children = new ODLGeom[n];
}
if(children[i]==null){
children[i] =new ODLLoadedGeometry(getJTSGeometry().getGeometryN(i));
}
return children[i];
}
public GeomType getGeomType(){
Geometry jts = getJTSGeometry();
if(jts!=null){
if(Point.class.isInstance(jts)){
return GeomType.POINT;
}
if(LineString.class.isInstance(jts)){
return GeomType.LINESTRING;
}
if(Polygon.class.isInstance(jts)){
return GeomType.POLYGON;
}
if(MultiPoint.class.isInstance(jts)){
return GeomType.MULTIPOINT;
}
if(MultiLineString.class.isInstance(jts)){
return GeomType.MULTILINESTRING;
}
if(MultiPolygon.class.isInstance(jts)){
return GeomType.MULTIPOLYGON;
}
if(GeometryCollection.class.isInstance(jts)){
return GeomType.COLLECTION;
}
}
return GeomType.INVALID;
}
public LatLong getPoint(int i){
Geometry jts = getJTSGeometry();
if(jts!=null){
Coordinate c = null;
if(LineString.class.isInstance(jts)){
// more efficient doing this...
c = ((LineString)jts).getCoordinateN(i);
}else{
c = jts.getCoordinates()[i];
}
return new LatLongImpl(c.y, c.x);
}
return null;
}
@Override
public int getNbHoles(){
throwIfNotPolygon();
return ((Polygon)getJTSGeometry()).getNumInteriorRing();
}
private void throwIfNotPolygon() {
if(getGeomType() != GeomType.POLYGON){
throw new RuntimeException("Cannot call polygon method on non-polygon geometry");
}
}
@Override
public ODLGeom getExterior(){
throwIfNotPolygon();
allocatePolygonPartsArray();
if(children[0]==null){
children[0] = new ODLLoadedGeometry(((Polygon)getJTSGeometry()).getExteriorRing());
}
return children[0];
}
private synchronized void allocatePolygonPartsArray(){
if(children==null){
int nholes = getNbHoles();
children = new ODLGeom[nholes+1];
}
}
@Override
public ODLGeom getHole(int i){
throwIfNotPolygon();
allocatePolygonPartsArray();
if(children[i+1]==null){
children[i+1] = new ODLLoadedGeometry(((Polygon)getJTSGeometry()).getInteriorRingN(i));
}
return children[i+1];
}
}