832 lines
24 KiB
Java
832 lines
24 KiB
Java
//This file is part of SECONDO.
|
|
|
|
//Copyright (C) 2004, University in Hagen, Department of Computer Science,
|
|
//Database Systems for New Applications.
|
|
|
|
//SECONDO 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 2 of the License, or
|
|
//(at your option) any later version.
|
|
|
|
//SECONDO 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 SECONDO; if not, write to the Free Software
|
|
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
package viewer.spacetimecube;
|
|
|
|
import java.awt.*;
|
|
import java.awt.geom.AffineTransform;
|
|
import java.awt.geom.Rectangle2D;
|
|
import java.awt.geom.Point2D;
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.File;
|
|
import java.net.URL;
|
|
import java.util.*;
|
|
import gui.idmanager.*;
|
|
import javax.imageio.ImageIO;
|
|
import project.OSMMercator;
|
|
import tools.downloadmanager.ActiveDownload;
|
|
import tools.downloadmanager.DownloadEvent;
|
|
import tools.downloadmanager.DownloadManager;
|
|
import tools.downloadmanager.DownloadObserver;
|
|
import tools.downloadmanager.DownloadState;
|
|
import tools.Pair;
|
|
import viewer.SpaceTimeCubeViewer;
|
|
|
|
/**
|
|
* Class representing a SpaceTimeCube in a 3D-World.
|
|
* @author Franz Fahrmeier
|
|
*
|
|
*/
|
|
public class SpaceTimeCube {
|
|
|
|
private boolean worldcoord; // stores if MPoint added to STC is based on world coordinates
|
|
private Vector<MPoint> mPointsVector; // list of all MPoints maintained in this SpaceTimeCube
|
|
/*
|
|
* Min and max coordinates of the MPoints, not the SpaceTimeCube itself!
|
|
* ...in world coordinates
|
|
*/
|
|
private double minXorig, maxXorig, minYorig, maxYorig;
|
|
private long minZorig, maxZorig;
|
|
// labels for axis' min and max values
|
|
private double minXtext, maxXtext, minYtext, maxYtext;
|
|
private String minZtext, maxZtext;
|
|
/*
|
|
* Min and max coordinates of the MPoints, not the SpaceTimeCube itself!
|
|
* ...in OSMMercator coordinates
|
|
*/
|
|
private double minXproj, maxXproj, minYproj, maxYproj;
|
|
/*
|
|
* Min/max values of SpaceTimeCube
|
|
* either in original coordinates or in OSMMercator coordinates.
|
|
*/
|
|
private double minX, minY, maxX, maxY;
|
|
private long minZ, maxZ;
|
|
/*
|
|
* Min and max values defined by the user in the GUI.
|
|
*/
|
|
private double minXlimit, maxXlimit, minYlimit, maxYlimit;
|
|
private long minZlimit, maxZlimit;
|
|
private boolean limitSet;
|
|
|
|
/*
|
|
* Min and max values without having a filter/limit set by the user.
|
|
*/
|
|
private double minXnoLimit, maxXnoLimit, minYnoLimit, maxYnoLimit;
|
|
private long minZnoLimit, maxZnoLimit;
|
|
|
|
private OSMMercator osmm;
|
|
private int tileAmount; // amount of tiles to download
|
|
private int downloadsDone; // each tile-download is counted
|
|
private SpaceTimeCubeViewer STCviewer;
|
|
private Vector<Vector> pointArrays; // stores arrays of points (Point3DSTC)
|
|
private int[] indexesAfterFilter; // stores which indexes in mPointsVector are still used after filtering
|
|
|
|
// shared variables for map download and generation
|
|
private int xCount, yCount;
|
|
private String[] tilePaths;
|
|
private double xySTCproj;
|
|
private LinkedList<Pair<URL, AffineTransform>> urls;
|
|
|
|
// variables for tile handling
|
|
private static final String PROTOCOL = "http";
|
|
private static final String SERVER = "tile.openstreetmap.org";
|
|
private static final int PORT = 80;
|
|
private static final String DIRECTORY = "/";
|
|
private static final String PREFIX = "";
|
|
private static final int MINZOOMLEVEL = 0;
|
|
private static final int MAXZOOMLEVEL = 18;
|
|
private static final int MAXDOWNLOADS = 2;
|
|
private static final int TILESIZEX = 256;
|
|
private static final int TILESIZEY = 256;
|
|
private static String PATH = "osmDowloads";
|
|
private static int CACHESIZE = 8 * 1024 * 1024; // 8 MB cache
|
|
|
|
/**
|
|
* Constructor for SpaceTimeCube.
|
|
* @param viewer
|
|
* viewer to put the SpaceTimeCube into.
|
|
*/
|
|
public SpaceTimeCube(SpaceTimeCubeViewer viewer) {
|
|
|
|
worldcoord = true;
|
|
mPointsVector = new Vector<MPoint>();
|
|
osmm = new OSMMercator();
|
|
STCviewer = viewer;
|
|
pointArrays = new Vector<Vector>();
|
|
limitSet = false;
|
|
|
|
}
|
|
|
|
/**
|
|
* @return
|
|
* true if SpaceTimeCube currently deals with world coordinates.
|
|
*/
|
|
public boolean isWorldcoord() { return worldcoord; }
|
|
|
|
/**
|
|
* Returns all arrays of points (Point3DSTC).
|
|
* @return
|
|
* arrays of points as vectors.
|
|
*/
|
|
public Vector<Vector> getPointArrays() { return pointArrays; }
|
|
|
|
/**
|
|
* Removes all MPoints with a given ID from SpaceTimeCube.
|
|
* @param id
|
|
* ID.
|
|
*/
|
|
public void removeMPoints(ID id) {
|
|
|
|
Vector<MPoint> tempMPoints = new Vector<MPoint>();
|
|
for (int i=0;i<mPointsVector.size();i++) {
|
|
MPoint tempMP = mPointsVector.get(i);
|
|
if (tempMP.getSecondoId().equals(id)) {
|
|
tempMPoints.add(tempMP);
|
|
}
|
|
}
|
|
for (int i=0;i<tempMPoints.size();i++) {
|
|
mPointsVector.remove(tempMPoints.get(i));
|
|
}
|
|
|
|
limitSet = false;
|
|
recompute();
|
|
}
|
|
|
|
/**
|
|
* Returns the length of the X-/Y-axis
|
|
* either in world coordinates or in OSMMercator coordinates
|
|
* depending if the STC is storing only world coordinates or not.
|
|
* @return
|
|
* length of the X-/Y-axis.
|
|
*/
|
|
public double getXYlength() {
|
|
double XYlength;
|
|
if (worldcoord) {
|
|
double XYwidth = Math.abs(maxXproj - minXproj);
|
|
double XYheight = Math.abs(maxYproj - minYproj);
|
|
|
|
if (XYwidth > XYheight) XYlength = XYwidth;
|
|
else XYlength = XYheight;
|
|
}
|
|
else {
|
|
if ((maxXorig-minXorig) > (maxYorig-minYorig)) XYlength = maxXorig-minXorig;
|
|
else XYlength = maxYorig-minYorig;
|
|
}
|
|
|
|
return XYlength;
|
|
}
|
|
|
|
/**
|
|
* Returns the length of the Z-axis in world coordinates.
|
|
* @return
|
|
* length of the Z-axis in world coordinates.
|
|
*/
|
|
public double getZlength() {
|
|
return (maxZorig-minZorig);
|
|
}
|
|
|
|
/**
|
|
* @return
|
|
* min value of X either in original coordinates or in OSMMercator coordinates.
|
|
*/
|
|
public double getMinX() { return minX; }
|
|
/**
|
|
* @return
|
|
* min value of Y either in original coordinates or in OSMMercator coordinates.
|
|
*/
|
|
public double getMinY() { return minY; }
|
|
/**
|
|
* @return
|
|
* max value of X either in original coordinates or in OSMMercator coordinates.
|
|
*/
|
|
public double getMaxX() { return maxX; }
|
|
/**
|
|
* @return
|
|
* max value of Y either in original coordinates or in OSMMercator coordinates.
|
|
*/
|
|
public double getMaxY() { return maxY; }
|
|
/**
|
|
* @return
|
|
* min value of Z in milliseconds since 01/01/1970.
|
|
*/
|
|
public long getMinZ() { return minZ; }
|
|
/**
|
|
* @return
|
|
* max value of Z in milliseconds since 01/01/1970.
|
|
*/
|
|
public long getMaxZ() { return maxZ; }
|
|
|
|
// Methods for publishing actual axis' start and end values.
|
|
public double getMinXtext() { return minXtext; }
|
|
public double getMinYtext() { return minYtext; }
|
|
public String getMinZtext() { return minZtext; }
|
|
public double getMaxXtext() { return maxXtext; }
|
|
public double getMaxYtext() { return maxYtext; }
|
|
public String getMaxZtext() { return maxZtext; }
|
|
|
|
/*
|
|
* Methods for publishing min and max values without limitation due to filtering.
|
|
* That means these are the values based on all currently existing MovingPoints.
|
|
*/
|
|
public double getMinXnoLimit() { return minXnoLimit; }
|
|
public double getMinYnoLimit() { return minYnoLimit; }
|
|
public long getMinZnoLimit() { return minZnoLimit; }
|
|
public double getMaxXnoLimit() { return maxXnoLimit; }
|
|
public double getMaxYnoLimit() { return maxYnoLimit; }
|
|
public long getMaxZnoLimit() { return maxZnoLimit; }
|
|
|
|
/*
|
|
* Methods for setting limits/filter.
|
|
*/
|
|
public void setMinXlimit(double d) { minXlimit = d; }
|
|
public void setMinYlimit(double d) { minYlimit = d; }
|
|
public void setMaxXlimit(double d) { maxXlimit = d; }
|
|
public void setMaxYlimit(double d) { maxYlimit = d; }
|
|
public void setMinZlimit(long l) { minZlimit = l; }
|
|
public void setMaxZlimit(long l) { maxZlimit = l; }
|
|
|
|
/*
|
|
* Methods for getting limits/filter.
|
|
*/
|
|
public double getMinXlimit() { return minXlimit; }
|
|
public double getMinYlimit() { return minYlimit; }
|
|
public double getMaxXlimit() { return maxXlimit; }
|
|
public double getMaxYlimit() { return maxYlimit; }
|
|
public long getMinZlimit() { return minZlimit; }
|
|
public long getMaxZlimit() { return maxZlimit; }
|
|
|
|
/**
|
|
* Defines that a filter was set.
|
|
* @param b
|
|
* true is filter set.
|
|
*/
|
|
public void setLimitSet(boolean b) { limitSet = b; }
|
|
|
|
/**
|
|
* @return
|
|
* true if a filter was set.
|
|
*/
|
|
public boolean getLimitSet() { return limitSet; }
|
|
|
|
/**
|
|
* Adds a vector of MovingPoints to the SpaceTimeCube.
|
|
* @param mpoints
|
|
* vector of MPoints
|
|
*/
|
|
public void addMPointsVector(Vector<MPoint> mpoints) {
|
|
for (int i=0;i<mpoints.size();i++) {
|
|
mPointsVector.add(mpoints.get(i));
|
|
}
|
|
|
|
recompute();
|
|
}
|
|
|
|
/**
|
|
* @return
|
|
* Vector including all moving points (MPoint).
|
|
*/
|
|
public Vector<MPoint> getMPointsVector() { return mPointsVector; }
|
|
|
|
/**
|
|
* @return
|
|
* Indexes in mPointsVector that are still used after filtering.
|
|
*/
|
|
public int[] getIndexesAfterFilter() { return indexesAfterFilter; }
|
|
|
|
/**
|
|
* Computations will be performed like
|
|
* - borders of the SpaceTimeCube as world coordinates and projected values
|
|
* - points of the MPoints are stored into a vector
|
|
* OSMMercator projection is used.
|
|
*/
|
|
public void recompute() {
|
|
|
|
if (mPointsVector.isEmpty()) worldcoord = true;
|
|
|
|
if (!limitSet) {
|
|
|
|
double minXnoLimitTemp=0, maxXnoLimitTemp=0, minYnoLimitTemp=0, maxYnoLimitTemp=0;
|
|
long minZnoLimitTemp=0, maxZnoLimitTemp=0;
|
|
|
|
for (int i=0;i<mPointsVector.size();i++) {
|
|
MPoint tempMP = mPointsVector.get(i);
|
|
double[] x = tempMP.getXarray().clone();
|
|
|
|
if (x.length > 0) {
|
|
double[] y = tempMP.getYarray().clone();
|
|
long[] z = tempMP.getMilliSecondsArray().clone();
|
|
|
|
Arrays.sort(x);
|
|
Arrays.sort(y);
|
|
Arrays.sort(z);
|
|
|
|
if (i==0) {
|
|
minXnoLimitTemp = x[0];
|
|
maxXnoLimitTemp = x[x.length-1];
|
|
minYnoLimitTemp = y[0];
|
|
maxYnoLimitTemp = y[y.length-1];
|
|
minZnoLimitTemp = z[0];
|
|
maxZnoLimitTemp = z[z.length-1];
|
|
}
|
|
|
|
if (x[0] < minXnoLimitTemp) minXnoLimitTemp = x[0];
|
|
if (x[x.length-1] > maxXnoLimitTemp) maxXnoLimitTemp = x[x.length-1];
|
|
if (y[0] < minYnoLimitTemp) minYnoLimitTemp = y[0];
|
|
if (y[y.length-1] > maxYnoLimitTemp) maxYnoLimitTemp = y[y.length-1];
|
|
if (z[0] < minZnoLimitTemp) minZnoLimitTemp = z[0];
|
|
if (z[z.length-1] > maxZnoLimitTemp) maxZnoLimitTemp = z[z.length-1];
|
|
|
|
}
|
|
}
|
|
|
|
minXnoLimit = minXnoLimitTemp;
|
|
maxXnoLimit = maxXnoLimitTemp;
|
|
minYnoLimit = minYnoLimitTemp;
|
|
maxYnoLimit = maxYnoLimitTemp;
|
|
minZnoLimit = minZnoLimitTemp;
|
|
maxZnoLimit = maxZnoLimitTemp;
|
|
|
|
minXlimit = minXnoLimit;
|
|
maxXlimit = maxXnoLimit;
|
|
minYlimit = minYnoLimit;
|
|
maxYlimit = maxYnoLimit;
|
|
minZlimit = minZnoLimit;
|
|
maxZlimit = maxZnoLimit;
|
|
}
|
|
|
|
int counter = 0;
|
|
int countInd = 0;
|
|
int lastInd = -1;
|
|
int pointCount = 0;
|
|
indexesAfterFilter = new int[mPointsVector.size()];
|
|
pointArrays.clear();
|
|
for (int i=0;i<mPointsVector.size();i++) {
|
|
MPoint tempMP = mPointsVector.get(i);
|
|
double[] x = tempMP.getXarray();
|
|
double[] y = tempMP.getYarray();
|
|
long[] z = tempMP.getMilliSecondsArray();
|
|
String[] t = tempMP.getTimesArray();
|
|
|
|
Point2D.Double p2d=null;
|
|
Vector<Point3DSTC> points = new Vector<Point3DSTC>();
|
|
double tolerance = 0.000000000001;
|
|
|
|
for (int a=0;a<x.length;a++) {
|
|
|
|
if (x[a] >= (minXlimit-tolerance) && x[a] <= (maxXlimit+tolerance) &&
|
|
y[a] >= (minYlimit-tolerance) && y[a] <= (maxYlimit+tolerance) &&
|
|
z[a] >= minZlimit && z[a] <= maxZlimit) {
|
|
|
|
if (lastInd != i) { countInd++; }
|
|
indexesAfterFilter[countInd-1] = i;
|
|
lastInd = i;
|
|
|
|
if (worldcoord) {
|
|
downloadsDone = 0;
|
|
if (x[a]<-180 || x[a] >180 || y[a]>90 || y[a]<-90) {
|
|
worldcoord = false;
|
|
recompute();
|
|
return;
|
|
}
|
|
p2d = new Point2D.Double();
|
|
osmm.project(x[a], y[a], p2d);
|
|
}
|
|
|
|
if (counter==0) {
|
|
minXorig = x[a];
|
|
maxXorig = x[a];
|
|
minYorig = y[a];
|
|
maxYorig = y[a];
|
|
minZorig = z[a];
|
|
maxZorig = z[a];
|
|
minZtext = t[a];
|
|
maxZtext = t[a];
|
|
if (worldcoord) {
|
|
minXproj = p2d.x;
|
|
maxXproj = p2d.x;
|
|
minYproj = p2d.y;
|
|
maxYproj = p2d.y;
|
|
}
|
|
}
|
|
|
|
if (x[a] < minXorig) { minXorig = x[a]; }
|
|
if (x[a] > maxXorig) { maxXorig = x[a]; }
|
|
if (y[a] < minYorig) { minYorig = y[a]; }
|
|
if (y[a] > maxYorig) { maxYorig = y[a]; }
|
|
if (z[a] < minZorig) {
|
|
minZorig = z[a];
|
|
minZtext = t[a];
|
|
}
|
|
if (z[a] > maxZorig) {
|
|
maxZorig = z[a];
|
|
maxZtext = t[a];
|
|
}
|
|
|
|
if (worldcoord) {
|
|
if (p2d.x < minXproj) { minXproj = p2d.x; }
|
|
if (p2d.x > maxXproj) { maxXproj = p2d.x; }
|
|
if (p2d.y < minYproj) { minYproj = p2d.y; }
|
|
if (p2d.y > maxYproj) { maxYproj = p2d.y; }
|
|
|
|
points.add(new Point3DSTC(p2d.x,p2d.y,z[a],tempMP.getSecondoId()));
|
|
}
|
|
else {
|
|
points.add(new Point3DSTC(x[a],y[a],z[a],tempMP.getSecondoId()));
|
|
}
|
|
counter++; // count up if a filter was met
|
|
}
|
|
}
|
|
if (points.size()>0) {
|
|
pointArrays.add(points);
|
|
pointCount += points.size();
|
|
}
|
|
}
|
|
|
|
System.out.println("Maintained points in STC: "+pointCount);
|
|
|
|
/*
|
|
* Some additional space at X and Y axis needs to be added
|
|
* so that the map will not immediately start with a trajectory.
|
|
* Background: Otherwise it's hard to determine where the trajectory starts.
|
|
*/
|
|
double factorAdd = 0.05;
|
|
double lengthXorig = maxXorig - minXorig;
|
|
double lengthYorig = maxYorig - minYorig;
|
|
double lengthXproj = maxXproj - minXproj;
|
|
double lengthYproj = maxYproj - minYproj;
|
|
minXorig -= (lengthXorig*factorAdd);
|
|
maxXorig += (lengthXorig*factorAdd);
|
|
minYorig -= (lengthYorig*factorAdd);
|
|
maxYorig += (lengthYorig*factorAdd);
|
|
minXproj -= (lengthXproj*factorAdd);
|
|
maxXproj += (lengthXproj*factorAdd);
|
|
minYproj -= (lengthYproj*factorAdd);
|
|
maxYproj += (lengthYproj*factorAdd);
|
|
|
|
if (worldcoord) {
|
|
minX = minXproj;
|
|
minY = minYproj;
|
|
maxX = maxXproj;
|
|
maxY = maxYproj;
|
|
}
|
|
else {
|
|
minX = minXorig;
|
|
minY = minYorig;
|
|
maxX = maxXorig;
|
|
maxY = maxYorig;
|
|
}
|
|
|
|
Point2D.Double p1 = new Point2D.Double();
|
|
if (worldcoord) osmm.getOrig(minX,minY,p1);
|
|
else p1.setLocation(minX,minY);
|
|
Point2D.Double p2 = new Point2D.Double();
|
|
if (worldcoord) osmm.getOrig(maxX,maxY,p2);
|
|
else p2.setLocation(maxX,maxY);
|
|
minXlimit = p1.getX();
|
|
maxXlimit = p2.getX();
|
|
minYlimit = p1.getY();
|
|
maxYlimit = p2.getY();
|
|
|
|
minXtext = minXorig;
|
|
minYtext = minYorig;
|
|
maxXtext = maxXorig;
|
|
maxYtext = maxYorig;
|
|
minZ = minZorig;
|
|
maxZ = maxZorig;
|
|
|
|
minZlimit = minZ;
|
|
maxZlimit = maxZ;
|
|
}
|
|
|
|
/**
|
|
* Downloads all necessary tiles from OSM tiles interface
|
|
* based on the currently maintained MPoints in the STC.
|
|
* @return
|
|
* true if all tiles have been downloaded or STC is not based on world coordinates
|
|
* false if downloads are going on
|
|
*/
|
|
public boolean downloadMap() {
|
|
/*
|
|
* Bounding box of the SpaceTimeCube in world coordinates is calculated (clipRect).
|
|
* Tile URLs are calculated and tiles downloaded (as per clipRect).
|
|
*/
|
|
if (worldcoord && mPointsVector.size()>0) {
|
|
DownloadManager downloadManager;
|
|
|
|
double widthSTCproj = Math.abs(maxXproj - minXproj);
|
|
double heightSTCproj = Math.abs(maxYproj - minYproj);
|
|
|
|
if (widthSTCproj > heightSTCproj) xySTCproj = widthSTCproj;
|
|
else xySTCproj = heightSTCproj;
|
|
|
|
Point2D.Double topleftSTCorig = new Point2D.Double();
|
|
Point2D.Double bottomrightSTCorig = new Point2D.Double();
|
|
osmm.getOrig(minXproj, minYproj+xySTCproj, topleftSTCorig);
|
|
osmm.getOrig(minXproj+xySTCproj, minYproj, bottomrightSTCorig);
|
|
|
|
double widthSTCorig = Math.abs(bottomrightSTCorig.x - topleftSTCorig.x);
|
|
double heightSTCorig = Math.abs(topleftSTCorig.y - bottomrightSTCorig.y);
|
|
maxXtext = minXtext + widthSTCorig;
|
|
maxYtext = minYtext + heightSTCorig;
|
|
|
|
// bounding box of the SpaceTimeCube in world coordinates
|
|
Rectangle2D.Double clipRect = new Rectangle2D.Double(topleftSTCorig.x, topleftSTCorig.y-heightSTCorig
|
|
, widthSTCorig, heightSTCorig);
|
|
|
|
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
|
|
int DIM_X = (int)dim.getWidth();
|
|
int DIM_Y = (int)dim.getHeight();
|
|
URL baseUrl=null;
|
|
try {
|
|
baseUrl = new URL(PROTOCOL, SERVER, PORT, DIRECTORY);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
String prefix = PREFIX;
|
|
|
|
StaticOSMMapper osmmapper = new StaticOSMMapper(TILESIZEX, TILESIZEY, DIM_X, DIM_Y,
|
|
MINZOOMLEVEL, MAXZOOMLEVEL, baseUrl, prefix);
|
|
|
|
try {
|
|
downloadManager = new DownloadManager(new File(PATH), MAXDOWNLOADS, false);
|
|
} catch (Exception e) {
|
|
System.err.println("Problem in initiating download manager");
|
|
downloadManager = null;
|
|
}
|
|
downloadManager.setConnectTimeout(5000);
|
|
downloadManager.setReadTimeout(5000);
|
|
|
|
DownloadObserver observer = new DownloadObserver() {
|
|
public void downloadStateChanged(DownloadEvent evt) {
|
|
SpaceTimeCube.this.downloadStateChanged(evt);
|
|
}
|
|
};
|
|
|
|
urls = osmmapper.computeURLs((Rectangle2D.Double) clipRect);
|
|
|
|
/*
|
|
* Check if amount of tile rows and columns is identical.
|
|
* If not a row/column needs to be added at the end of urls
|
|
* to get a quadratic image finally.
|
|
*/
|
|
int z=0, x0=0, y0=0, x=0, y=0, counter;
|
|
|
|
do {
|
|
counter=0;
|
|
Iterator<Pair<URL, AffineTransform>> it2 = urls.iterator();
|
|
|
|
while (it2.hasNext()) {
|
|
URL url = it2.next().first();
|
|
String temp = url.getPath();
|
|
temp = temp.substring(1, temp.lastIndexOf("."));
|
|
String[] s = temp.split("\\/");
|
|
z = Integer.parseInt(s[0]);
|
|
x = Integer.parseInt(s[1]);
|
|
y = Integer.parseInt(s[2]);
|
|
if (counter==0) {
|
|
x0=x;
|
|
y0=y;
|
|
}
|
|
counter++;
|
|
}
|
|
xCount = x-x0+1;
|
|
yCount = y-y0+1;
|
|
if (xCount>yCount) {
|
|
Rectangle2D.Double rec = getBBoxForTile(x,y+1,z);
|
|
clipRect.height = clipRect.height + rec.height;
|
|
urls = osmmapper.computeURLs((Rectangle2D.Double) clipRect);
|
|
}
|
|
if (yCount>xCount) {
|
|
Rectangle2D.Double rec = getBBoxForTile(x+1,y,z);
|
|
clipRect.width = clipRect.width + rec.width;
|
|
urls = osmmapper.computeURLs((Rectangle2D.Double) clipRect);
|
|
}
|
|
} while (xCount != yCount);
|
|
|
|
Iterator<Pair<URL, AffineTransform>> it = urls.iterator();
|
|
|
|
tileAmount = urls.size();
|
|
downloadsDone = 0;
|
|
while (it.hasNext()) {
|
|
URL url = it.next().first();
|
|
File f = downloadManager.getURL(url, observer);
|
|
if (f == null) STCviewer.showLoadingDialog(true);
|
|
else if (f != null) { // url already downloaded
|
|
downloadsDone++;
|
|
}
|
|
if (downloadsDone == tileAmount) {
|
|
STCviewer.showLoadingDialog(false);
|
|
return true;
|
|
}
|
|
}
|
|
return false; // downloads are going on
|
|
}
|
|
else return true; // STC is not based on world coordinates
|
|
}
|
|
|
|
/**
|
|
* Generates an image/map cropped according to the size of the SpaceTimeCube.
|
|
* Steps:
|
|
* - bounding box of all tiles in world coordinates is calculated (bboxTilesOrig)
|
|
* - BufferedImage is built out of single tiles (result)
|
|
* - Built image is cropped to the size of the SpaceTimeCube
|
|
* @return
|
|
* map as BufferedImage.
|
|
*/
|
|
public BufferedImage generateMap() {
|
|
if (worldcoord) {
|
|
|
|
Iterator<Pair<URL, AffineTransform>> it = urls.iterator();
|
|
double xImage=0, yImage=0;
|
|
int counter=0;
|
|
int x=0, y=0, z=0;
|
|
double[] heights = new double[urls.size()];
|
|
double[] widths = new double[urls.size()];
|
|
tilePaths = new String[urls.size()];
|
|
while (it.hasNext()) {
|
|
URL url = it.next().first();
|
|
String temp = url.getPath();
|
|
tilePaths[counter] = temp;
|
|
temp = temp.substring(1, temp.lastIndexOf("."));
|
|
String[] s = temp.split("\\/");
|
|
z = Integer.parseInt(s[0]);
|
|
x = Integer.parseInt(s[1]);
|
|
y = Integer.parseInt(s[2]);
|
|
Rectangle2D.Double bboxTile = getBBoxForTile(x, y, z);
|
|
|
|
if (counter==0) {
|
|
xImage = bboxTile.x;
|
|
yImage = bboxTile.y;
|
|
} else {
|
|
if (bboxTile.x < xImage) {
|
|
xImage = bboxTile.x;
|
|
}
|
|
if (bboxTile.y > yImage) {
|
|
yImage = bboxTile.y;
|
|
}
|
|
}
|
|
widths[counter] = bboxTile.width;
|
|
heights[counter] = bboxTile.height;
|
|
|
|
counter++;
|
|
}
|
|
|
|
double width=0;
|
|
double height=0;
|
|
|
|
// width of a tile is always same (in world coordinates)!
|
|
if (widths.length > 0) width = widths[0] * xCount;
|
|
|
|
// height calculation is only done for the first column (which is enough)
|
|
for (int i=0; i<yCount; i++) {
|
|
height+= heights[i];
|
|
}
|
|
|
|
if (heights.length > 0) yImage+=heights[0];
|
|
|
|
int imageXY = xCount * TILESIZEX;
|
|
|
|
// bounding box of all tiles in world coordinates
|
|
Rectangle2D.Double bboxTilesOrig = new Rectangle2D.Double(xImage, yImage, width, height);
|
|
|
|
Point2D.Double topleftTilesProj = new Point2D.Double();
|
|
Point2D.Double bottomrightTilesProj = new Point2D.Double();
|
|
osmm.project(bboxTilesOrig.x, bboxTilesOrig.y, topleftTilesProj);
|
|
osmm.project(bboxTilesOrig.x+bboxTilesOrig.width, bboxTilesOrig.y-bboxTilesOrig.height, bottomrightTilesProj);
|
|
|
|
BufferedImage result = new BufferedImage(imageXY, imageXY, BufferedImage.TYPE_INT_RGB);
|
|
Graphics2D g = result.createGraphics();
|
|
|
|
int xPos=0;
|
|
int yPos=0;
|
|
for (int i=0;i<tilePaths.length;i++) {
|
|
BufferedImage bi=null;
|
|
try {
|
|
bi = ImageIO.read(new File("./"+PATH+"/"+PROTOCOL+"/"+SERVER+"/"+PORT+tilePaths[i]));
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
g.drawImage(bi, null, xPos, yPos);
|
|
|
|
yPos += TILESIZEY;
|
|
if(yPos >= result.getHeight()){
|
|
yPos = 0;
|
|
xPos += TILESIZEX;
|
|
}
|
|
}
|
|
|
|
double xyImageProj = Math.abs(bottomrightTilesProj.x - topleftTilesProj.x);
|
|
|
|
double offsetLeft = Math.abs(minXproj - topleftTilesProj.x);
|
|
double offsetBottom = Math.abs(minYproj - bottomrightTilesProj.y);
|
|
double subimgXY;
|
|
|
|
offsetLeft = imageXY/xyImageProj*offsetLeft;
|
|
offsetBottom = imageXY/xyImageProj*offsetBottom;
|
|
subimgXY = imageXY/xyImageProj*xySTCproj;
|
|
|
|
/*
|
|
* We start drawing lines/mpoints from x,y,z=0.
|
|
* That means offsetTop has to be dependent on offsetBottom.
|
|
*/
|
|
double offsetTop = imageXY - subimgXY - offsetBottom;
|
|
|
|
result = result.getSubimage((int)Math.round(offsetLeft), (int)Math.round(offsetTop),
|
|
(int)Math.round(subimgXY), (int)Math.round(subimgXY));
|
|
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Removes all objects(MPoints) from SpaceTimeCube.
|
|
*/
|
|
public void clear() {
|
|
mPointsVector.clear();
|
|
pointArrays.clear();
|
|
limitSet = false;
|
|
recompute();
|
|
}
|
|
|
|
/**
|
|
* Callback method. Called when a state of a pending download is changed.
|
|
*
|
|
* @param evt
|
|
* Event to handle.
|
|
**/
|
|
private void downloadStateChanged(DownloadEvent evt) {
|
|
DownloadState state = evt.getState();
|
|
ActiveDownload ad = evt.getSource();
|
|
if (state == DownloadState.DONE) {
|
|
downloadsDone++;
|
|
if (downloadsDone == tileAmount) {
|
|
STCviewer.showLoadingDialog(false);
|
|
STCviewer.recompute();
|
|
}
|
|
}
|
|
else if (state == DownloadState.BROKEN) {
|
|
System.out.println("Download broken: " + ad.getURL());
|
|
System.out.println("Exception: " + evt.getException());
|
|
if (evt.getException() != null) {
|
|
evt.getException().printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* compute the bounding box covered by a specified tile in world coordinates.
|
|
*
|
|
* @param x
|
|
* X index of a tile
|
|
* @param y
|
|
* Y index of a tile
|
|
* @param z
|
|
* zoom level
|
|
* @return The images's MBR in world coordinates
|
|
**/
|
|
private Rectangle2D.Double getBBoxForTile(int x, int y, int zoom) {
|
|
double north = tile2lat(y, zoom);
|
|
double south = tile2lat(y + 1, zoom);
|
|
double west = tile2lon(x, zoom);
|
|
double east = tile2lon(x + 1, zoom);
|
|
return new Rectangle2D.Double(west, south, east - west, north - south);
|
|
}
|
|
|
|
/**
|
|
* computes the western boundary of a specified tile in world coordinates.
|
|
*
|
|
* @param x
|
|
* The map tile's X-index
|
|
* @param z
|
|
* The zoom level
|
|
* @return The longitude of the western boundary of a map tile
|
|
**/
|
|
private static double tile2lon(int x, int z) {
|
|
return x / Math.pow(2.0, z) * 360.0 - 180;
|
|
}
|
|
|
|
/**
|
|
* computes the northern boundary of a specified tile in world coordinates.
|
|
*
|
|
* @param x
|
|
* The map tile's Y-index
|
|
* @param z
|
|
* The zoom level
|
|
* @return The latitude of the northern boundary of a map tile
|
|
**/
|
|
private static double tile2lat(int y, int z) {
|
|
double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
|
|
return Math.toDegrees(Math.atan(Math.sinh(n)));
|
|
}
|
|
|
|
}
|