/*******************************************************************************
* 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 3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
******************************************************************************/
package com.opendoorlogistics.core.geometry.rog;
import java.awt.geom.Point2D;
import java.io.DataInputStream;
import java.io.File;
import com.opendoorlogistics.api.geometry.LatLong;
import com.opendoorlogistics.api.geometry.LatLongToScreen;
import com.opendoorlogistics.core.cache.ApplicationCache;
import com.opendoorlogistics.core.cache.RecentlyUsedCache;
import com.opendoorlogistics.core.geometry.ODLLoadableGeometry;
import com.opendoorlogistics.core.geometry.Spatial;
import com.opendoorlogistics.core.gis.map.OnscreenGeometry;
import com.opendoorlogistics.core.gis.map.data.LatLongImpl;
import com.opendoorlogistics.core.utils.Numbers;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
public class ODLRenderOptimisedGeom extends ODLLoadableGeometry {
public final static int INVALID_GEOMETRY=-1;
public final static int SUBPIXEL=-2;
public final static int USE_LAST_LEVEL=-3;
private final long id;
private final int wgsBlockNb;
private final int wgsGeomNbInBlock;
private final int [] blockNbsByZoom;
private final int [] geomNbInBlockByZoom;
private final QuadLoader loader;
private final int nbPoints;
private final int pointGeomsCount;
private final int polysGeomsCount;
private final int linestringsGeomsCount;
private final byte[] bjsonBytes;
@Override
public long getEstimatedSizeInBytes() {
long ret=8 + 4 + 8 + 4*blockNbsByZoom.length + 4 + geomNbInBlockByZoom.length*4 + 4;
// if(fullGeometry!=null){
// ret += Spatial.getEstimatedSizeInBytes(fullGeometry);
// }
return ret;
}
public ODLRenderOptimisedGeom(DataInputStream dis, QuadLoader loader) {
this.loader= loader;
try {
// read id
id = dis.readLong();
// read number of points
nbPoints = dis.readInt();
pointGeomsCount = dis.readInt();
linestringsGeomsCount = dis.readInt();
polysGeomsCount = dis.readInt();
// read bounds
double minX = dis.readDouble();
double minY = dis.readDouble();
double w = dis.readDouble();
double h = dis.readDouble();
wgsBounds = new Envelope(minX, minX +w, minY, minY + h);
// read latitude
double lng = dis.readDouble();
double lat = dis.readDouble();
wgsCentroid = new LatLongImpl(lat, lng);
// read wgs geometry position
wgsBlockNb = dis.readInt();
wgsGeomNbInBlock = dis.readInt();
// read position array size
byte sz = dis.readByte();
blockNbsByZoom = new int[sz];
geomNbInBlockByZoom = new int[sz];
for(int i =0 ; i<sz ; i++){
blockNbsByZoom[i] = dis.readInt();
}
for(int i =0 ; i<sz ; i++){
geomNbInBlockByZoom[i] = dis.readInt();
}
// binary json
bjsonBytes = RogReaderUtils.readBytesArray(dis);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static class ROGGeomKey{
final File file;
final long id;
ROGGeomKey(File file, long id) {
this.file = file;
this.id = id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((file == null) ? 0 : file.hashCode());
result = prime * result + (int) (id ^ (id >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ROGGeomKey other = (ROGGeomKey) obj;
if (file == null) {
if (other.file != null)
return false;
} else if (!file.equals(other.file))
return false;
if (id != other.id)
return false;
return true;
}
}
@Override
public synchronized Geometry getJTSGeometry() {
// This loads the full geometry and should be avoided wherever possible.
// The geometry is cached with a limited cache size.
// Try getting from the cache first
ROGGeomKey key = new ROGGeomKey(getFileInformation().getFile(), id);
RecentlyUsedCache cache = ApplicationCache.singleton().get(ApplicationCache.ROG_FULL_GEOMETRY);
Geometry ret = (Geometry)cache.get(key);
// Load if not cached
if(ret==null && wgsBlockNb!=-1){
ret = loader.loadGeometry(id,wgsBlockNb,wgsGeomNbInBlock );
if(ret!=null){
cache.put(key, ret, Spatial.getEstimatedSizeInBytes(ret));
}
}
return ret;
}
@Override
public boolean isLoaded() {
return true;
}
@Override
public OnscreenGeometry createOnscreenGeometry(LatLongToScreen converter) {
// lookup optimised for size
Long zoom = Numbers.toLong(converter.getZoomHashmapKey());
if(zoom!=null && zoom>=0 && zoom<blockNbsByZoom.length){
int iZoom = zoom.intValue();
int blockNb = blockNbsByZoom[iZoom];
Geometry g=null;
boolean drawFilledBounds=false;
if(blockNb == SUBPIXEL){
drawFilledBounds = true;
}
else if(blockNb >=0){
// take the exact geometry
g = loader.loadGeometry(id,blockNb, geomNbInBlockByZoom[iZoom]);
}
else{
// find nearest and transform
int nearestZoom = -1;
// look bigger first (lower index zoom is bigger within jxmapviewer project)
for(int i = iZoom-1 ; i>=0 && nearestZoom==-1 ; i--){
if(blockNbsByZoom[i]>=0){
nearestZoom = i;
}
}
// then look smaller
for(int i = iZoom + 1; i < blockNbsByZoom.length && nearestZoom==-1 ;i++){
if(blockNbsByZoom[i]>=0){
nearestZoom = i;
}
}
if(nearestZoom==-1){
// this should probably never happen
drawFilledBounds = true;
}else{
g = loader.loadTransformedGeometry(id,blockNbsByZoom[nearestZoom],geomNbInBlockByZoom[nearestZoom], nearestZoom, iZoom);
}
}
if(drawFilledBounds){
// create a geometry which is just a single point at the centroid (following renderer convention)
Point2D centroid = getWorldBitmapCentroid(converter);
GeometryFactory factory = new GeometryFactory();
g= factory.createPoint(new Coordinate(centroid.getX(),centroid.getY()));
}
return new OnscreenGeometry(g, drawFilledBounds);
}
else{
// have to use the full geometry (slow)
return new OnscreenGeometry(this, converter);
}
//return null;
}
@Override
public int getPointsCount() {
return nbPoints;
}
@Override
public LatLong getWGSCentroid() {
return wgsCentroid;
}
@Override
public Envelope getWGSBounds() {
return wgsBounds;
}
@Override
public boolean isLineString(){
return linestringsGeomsCount==1 && pointGeomsCount==0 && polysGeomsCount==0;
}
@Override
public int getAtomicGeomCount(AtomicGeomType type) {
switch(type){
case POINT:
return pointGeomsCount;
case LINESTRING:
return linestringsGeomsCount;
case POLYGON:
return polysGeomsCount;
}
return 0;
}
public int[] getFilePosition(int zoom){
if(zoom<0){
return new int[]{wgsBlockNb,wgsGeomNbInBlock};
}
else if (zoom < blockNbsByZoom.length){
return new int[]{blockNbsByZoom[zoom], geomNbInBlockByZoom[zoom]};
}
return null;
}
public long getGeomId(){
return id;
}
public byte[] getBjsonBytes(){
return bjsonBytes;
}
public RogFileInformation getFileInformation(){
return loader;
}
}