/*
* Copyright (C) 2014 Alec Dhuse
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.foldingmap.map.vector;
import co.foldingmap.map.MapProjection;
import co.foldingmap.map.MapView;
import co.foldingmap.map.MercatorProjection;
import co.foldingmap.map.tile.TileMath;
import co.foldingmap.xml.XmlOutput;
/**
* This class is the implementation of the KML Region object. It is used to
* determine if object should be shown at current view levels.
*
* Fading is not fully implemented at this time.
*
* @author Alec
*/
public class Region {
public static final int MIN_VIEW = 0;
public static final int MAX_VIEW = 1;
public static final int BOTH_VIEW = 2;
public static final int NONE_VIEW = 3;
private LatLonAltBox latLonAltBox;
private LevelOfDetail levelOfDetail;
private String regionName;
/**
* Constructor for objects of class Region
*/
public Region(String regionName, LatLonAltBox latLonAltBox, float maxLevelOfDetailPixels, float minLevelOfDetailPixels) {
this.regionName = regionName;
this.latLonAltBox = latLonAltBox;
this.levelOfDetail = new LevelOfDetail(maxLevelOfDetailPixels, minLevelOfDetailPixels);
this.setMaxLevelOfDetailPixels(maxLevelOfDetailPixels);
this.setMinLevelOfDetailPixels(minLevelOfDetailPixels);
}
/**
* Constructor for objects of class Region
*/
public Region(String regionName, LatLonAltBox latLonAltBox, LevelOfDetail lod) {
this.regionName = regionName;
this.latLonAltBox = latLonAltBox;
this.levelOfDetail = lod;
this.setMaxLevelOfDetailPixels(lod.getMaxLodPixels());
this.setMinLevelOfDetailPixels(lod.getMinLodPixels());
}
/**
* Constructor for objects of class Region
*/
public Region(String regionName) {
this.regionName = regionName;
this.levelOfDetail = new LevelOfDetail(-1, 0);
}
/**
* Calculates the diagonal distance of the LatLonAltBox on the screen.
*
* @param mapView
* @return
*/
public float calculateLevelOfDetailPixels(MapView mapView) {
float maxLOD;
double x1, x2, y1, y2;
y1 = mapView.getY(new Coordinate(0, latLonAltBox.getNorth(), latLonAltBox.getWest()));
y2 = mapView.getY(new Coordinate(0, latLonAltBox.getSouth(), latLonAltBox.getEast()));
x1 = mapView.getX(new Coordinate(0, latLonAltBox.getNorth(), latLonAltBox.getWest()), MapView.NO_WRAP);
x2 = mapView.getX(new Coordinate(0, latLonAltBox.getSouth(), latLonAltBox.getEast()), MapView.NO_WRAP);
maxLOD = (float) Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
return maxLOD;
}
/**
* Finds the Level of Detail for a bounds at a given tile zoom level.
*
* @param tileZoom
* @param bounds
* @return
*/
public static float calculateLodFromTileZoom(float tileZoom, LatLonAltBox bounds) {
float vectorZoom = TileMath.getVectorMapZoom(tileZoom);
MapProjection projection = new MercatorProjection();
projection.setZoomLevel(vectorZoom);
float y1 = (float) projection.getY(bounds.getNorthWestCoordinate());
float y2 = (float) projection.getY(bounds.getSouthEastCoordinate());
float x1 = (float) projection.getX(bounds.getNorthWestCoordinate());
float x2 = (float) projection.getX(bounds.getSouthEastCoordinate());
float lod = (float) Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
return lod;
}
/**
* Calculates a vector zoom level that will meet the given Level of Detail.
*
* @param lod
* @return
*/
public float calculateVectorZoomLevelForLOD(Float lod) {
MapProjection projection = new MercatorProjection();
float zoomLevel = 0.001f;
float testLod = 0;
float increment = 100;
while (increment > 0.001f) {
projection.setZoomLevel(zoomLevel);
float y1 = (float) projection.getY(latLonAltBox.getNorthWestCoordinate());
float y2 = (float) projection.getY(latLonAltBox.getSouthEastCoordinate());
float x1 = (float) projection.getX(latLonAltBox.getNorthWestCoordinate());
float x2 = (float) projection.getX(latLonAltBox.getSouthEastCoordinate());
testLod = (float) Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
if (testLod < lod) {
zoomLevel += increment;
} else {
zoomLevel -= increment;
increment *= 0.1f;
}
}
return zoomLevel;
}
/**
* Returns is this Region is equal to a given Object.
*
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
boolean isEqual = false;
if (o instanceof Region) {
Region r = (Region) o;
if (r.getName().equals(this.getName()) &&
r.getLatLonAltBox().equals(this.getLatLonAltBox()) &&
r.getLevelOfDetail().equals(this.getLevelOfDetail())) {
isEqual = true;
}
}
return isEqual;
}
/**
* Returns this Region's LAtLonAltBox
*
* @return
*/
public LatLonAltBox getLatLonAltBox() {
return latLonAltBox;
}
/**
* Returns the LevelOfDetail object for this Region.
*
* @return
*/
public LevelOfDetail getLevelOfDetail() {
return levelOfDetail;
}
public boolean isVisible(MapView mapView) {
boolean regionVisible;
double distance;
distance = this.calculateLevelOfDetailPixels(mapView);
regionVisible = false;
//-1 indecates the there is no value
if (levelOfDetail.getMaxLodPixels() == -1) {
//check to see if the pixels are within range
if ((levelOfDetail.getMinLodPixels() < distance))
regionVisible = true;
} else if (levelOfDetail.getMaxLodPixels() == -1) {
//check to see if the pixels are within range
if ( (levelOfDetail.getMaxLodPixels() > distance))
regionVisible = true;
} else {
//check to see if the pixels are within range
if ( (levelOfDetail.getMaxLodPixels() > distance) &&
(levelOfDetail.getMinLodPixels() < distance)) {
regionVisible = true;
}
}
return regionVisible;
}
/**
* Sets this Region's Max Level of Detail Pixels.
*
* @param lodMax
*/
public final void setMaxLevelOfDetailPixels(float lodMax) {
levelOfDetail.setMaxLodPixels(lodMax);
}
/**
* Sets this Region's Min Level of Detail Pixels.
*
* @param lodMin
*/
public final void setMinLevelOfDetailPixels(float lodMin) {
levelOfDetail.setMinLodPixels(lodMin);
}
/**
* Returns the regionName of this Region.
*
* @return
*/
public String getName() {
return regionName;
}
/**
* Writes this Region as is KML representation.
*
* @param kmlWriter
*/
public void toXML(XmlOutput kmlWriter) {
kmlWriter.openTag ("Region");
kmlWriter.writeTag("name", regionName);
latLonAltBox.toXML(kmlWriter);
levelOfDetail.toXML(kmlWriter);
kmlWriter.closeTag("Region");
}
// public void updateRegion(DigitalMap mapData, VectorObject mapObject, int viewType) {
// MapView mapView;
// Region objectRegion;
//
// mapView = mapData.getLastMapView();
// objectRegion = this;
//
// switch (viewType) {
// case MIN_VIEW:
// objectRegion.setMaxLevelOfDetailPixels(-1);
// objectRegion.setMinLevelOfDetailPixels(calculateLevelOfDetailPixels(mapView));
// break;
// case MAX_VIEW:
// objectRegion.setMaxLevelOfDetailPixels(calculateLevelOfDetailPixels(mapView));
// objectRegion.setMinLevelOfDetailPixels(-1);
// break;
// case BOTH_VIEW:
// objectRegion.setMaxLevelOfDetailPixels(calculateLevelOfDetailPixels(mapView));
// objectRegion.setMinLevelOfDetailPixels(calculateLevelOfDetailPixels(mapView));
// break;
// case NONE_VIEW:
// objectRegion.setMaxLevelOfDetailPixels(-1);
// objectRegion.setMinLevelOfDetailPixels(-1);
// break;
// }
//
// //mapObject.setRegion(this);
// }
}