Rev 595 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2009, Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* 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.
*/
package uk.me.parabola.splitter;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.tools.bzip2.CBZip2InputStream;
/**
* Some miscellaneous functions that are used within the .img code.
*
* @author Steve Ratcliffe
*/
public class Utils {
private static final NumberFormat FORMATTER = NumberFormat.getIntegerInstance();
public static final int MIN_LAT_MAP_UNITS = toMapUnit(-90);
public static final int MAX_LAT_MAP_UNITS = toMapUnit(90);
public static final int MIN_LON_MAP_UNITS = toMapUnit(-180);
public static final int MAX_LON_MAP_UNITS = toMapUnit(180);
public static String format(int number) {
return FORMATTER.format(number);
}
public static String format(long number) {
return FORMATTER.format(number);
}
public static double toDegrees(int val) {
return 360.0d * val / (1 << 24) ;
}
/**
* A map unit is an integer value that is 1/(2^24) degrees of latitude or
* longitude.
*
* @param l The lat or long as decimal degrees.
* @return An integer value in map units.
*/
public static int toMapUnit(double l) {
double delta = 360.0D / (1 << 24) / 2; // Correct rounding
if (l > 0)
return (int) ((l + delta) * (1 << 24) / 360);
return (int) ((l - delta) * (1 << 24) / 360);
}
/**
* Open a file and apply filters necessary to reading it such as decompression.
*
* @param name The file to open. gz, zip, bz2 are supported.
* @return A stream that will read the file, positioned at the beginning.
* @throws IOException If the file cannot be opened for any reason.
*/
@SuppressWarnings("resource")
public static Reader openFile(String name, boolean backgroundReader) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(name), 8192);
if (name.endsWith(".gz")) {
try {
is = new GZIPInputStream(is);
} catch (IOException e) {
throw new IOException( "Could not read " + name + " as a gz compressed file", e);
}
} else if (name.endsWith(".bz2")) {
try {
is.read(); is.read();
is = new CBZip2InputStream(is);
} catch (IOException e) {
throw new IOException( "Could not read " + name + " as a bz2 compressed file", e);
}
} else if (name.endsWith(".zip")) {
ZipInputStream zis = new ZipInputStream(is);
name = new File(name).getName(); // Strip off any path
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().startsWith(name.substring(0, name.length() - 4))) {
is = zis;
break;
}
}
if (is != zis) {
zis.close();
throw new IOException("Unable to find a file inside " + name + " that starts with " + name.substring(0, name.length() - 4));
}
}
if (backgroundReader) {
is = new BackgroundInputStream(is);
}
return new InputStreamReader(is, StandardCharsets.UTF_8);
}
public static Rectangle area2Rectangle (Area area, int overlap){
return new Rectangle(area.getMinLong()-overlap, area.getMinLat()-overlap,area.getWidth()+2*overlap,area.getHeight()+2*overlap);
}
/**
* Convert area into a list of polygons each represented by a list
* of points. It is possible that the area contains multiple discontinuous
* polygons, so you may append more than one shape to the output list.<br/>
* <b>Attention:</b> The outline of the polygon has clockwise order whereas
* holes in the polygon have counterclockwise order.
*
* Taken from Java2DConverter by WanMil in mkgmap
* @param area The area to be converted.
* @return a list of closed polygons
*/
public static List<List<Point>> areaToShapes(java.awt.geom.Area area) {
List<List<Point>> outputs = new ArrayList<>();
float[] res = new float[6];
PathIterator pit = area.getPathIterator(null);
List<Point> points = null;
int iPrevLat = Integer.MIN_VALUE;
int iPrevLong = Integer.MIN_VALUE;
while (!pit.isDone()) {
int type = pit.currentSegment(res);
float fLat = res[1];
float fLon = res[0];
int iLat = Math.round(fLat);
int iLon = Math.round(fLon);
switch (type) {
case PathIterator.SEG_LINETO:
if (iPrevLat != iLat || iPrevLong != iLon)
points.add(new Point(iLon,iLat));
iPrevLat = iLat;
iPrevLong = iLon;
break;
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_CLOSE:
if ((type == PathIterator.SEG_MOVETO && points != null) || type == PathIterator.SEG_CLOSE) {
if (points.size() > 2 && !points.get(0).equals(points.get(points.size() - 1))) {
points.add(points.get(0));
}
if (points.size() > 3){
outputs.add(points);
}
}
if (type == PathIterator.SEG_MOVETO){
points = new ArrayList<>();
points.add(new Point(iLon,iLat));
iPrevLat = iLat;
iPrevLong = iLon;
} else {
points = null;
iPrevLat = Integer.MIN_VALUE;
iPrevLong = Integer.MIN_VALUE;
}
break;
default:
System.out.println("Unsupported path iterator type " + type
+ ". This is an internal splitter error.");
}
pit.next();
}
return outputs;
}
/**
* Convert list of points which describe a closed polygon to an area
* Taken from Java2DConverter by WanMil in mkgmap
* @param shape
* @return
*/
public static java.awt.geom.Area shapeToArea(List<Point> shape){
Polygon polygon = new Polygon();
for (Point point : shape) {
polygon.addPoint(point.x, point.y);
}
return new java.awt.geom.Area(polygon);
}
/**
* Convert area with coordinates in degrees to area in MapUnits.
* @param area
* @return
*/
public static java.awt.geom.Area AreaDegreesToMapUnit(java.awt.geom.Area area){
if (area == null)
return null;
double[] res = new double[6];
Path2D path = new Path2D.Double();
PathIterator pit = area.getPathIterator(null);
while (!pit.isDone()) {
int type = pit.currentSegment(res);
double fLat = res[1];
double fLon = res[0];
int lat = toMapUnit(fLat);
int lon = toMapUnit(fLon);
switch (type) {
case PathIterator.SEG_LINETO:
path.lineTo(lon, lat);
break;
case PathIterator.SEG_MOVETO:
path.moveTo(lon, lat);
break;
case PathIterator.SEG_CLOSE:
path.closePath();
break;
default:
System.out.println("Unsupported path iterator type " + type
+ ". This is an internal splitter error.");
}
pit.next();
}
return new java.awt.geom.Area(path);
}
// returns true if the way is a closed polygon with a clockwise
// direction
public static boolean clockwise(List<Point> points) {
if(points.size() < 3 || !points.get(0).equals(points.get(points.size() - 1)))
return false;
long area = 0;
Point p1 = points.get(0);
for(int i = 1; i < points.size(); ++i) {
Point p2 = points.get(i);
area += ((long)p1.x * p2.y-
(long)p2.x * p1.y);
p1 = p2;
}
// this test looks to be inverted but gives the expected result!
// empty linear areas are defined as clockwise
return area <= 0;
}
public static void printMem(){
long maxMem = Runtime.getRuntime().maxMemory() / 1024 / 1024;
long totalMem = Runtime.getRuntime().totalMemory() / 1024 / 1024;
long freeMem = Runtime.getRuntime().freeMemory() / 1024 / 1024;
long usedMem = totalMem - freeMem;
System.out.println(" JVM Memory Info: Current " + totalMem + "MB (" + usedMem + "MB used, " + freeMem + "MB free) Max " + maxMem + "MB");
}
}