Rev 2633 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2006 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 2 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.
*
*
* Author: Steve Ratcliffe
* Create date: 11-Dec-2006
*/
package uk.me.parabola.imgfmt.app;
import java.util.Formatter;
import java.util.Locale;
import uk.me.parabola.imgfmt.Utils;
/**
* A point coordinate in unshifted map-units.
* A map unit is 360/2^24 degrees. In some places <i>shifted</i> coordinates
* are used, which means that they are divided by some power of two to save
* space in the file.
*
* You can create one of these with lat/long by calling the constructor with
* double args.
*
* This is an immutable class.
*
* @author Steve Ratcliffe
*/
public class Coord
implements Comparable<Coord
> {
private final static byte ON_BOUNDARY_MASK = 0x01
; // bit in flags is true if point lies on a boundary
private final static byte PRESERVED_MASK = 0x02
; // bit in flags is true if point should not be filtered out
private final static byte REPLACED_MASK = 0x04
; // bit in flags is true if point was replaced
private final static byte TREAT_AS_NODE_MASK = 0x08
; // bit in flags is true if point should be treated as a node
private final static byte FIXME_NODE_MASK = 0x10
; // bit in flags is true if a node with this coords has a fixme tag
private final int latitude
;
private final int longitude
;
private byte highwayCount
; // number of highways that use this point
private byte flags
; // further attributes
/**
* Construct from co-ordinates that are already in map-units.
* @param latitude The latitude in map units.
* @param longitude The longitude in map units.
*/
public Coord
(int latitude,
int longitude
) {
this.
latitude = latitude
;
this.
longitude = longitude
;
}
/**
* Construct from regular latitude and longitude.
* @param latitude The latitude in degrees.
* @param longitude The longitude in degrees.
*/
public Coord
(double latitude,
double longitude
) {
this.
latitude = Utils.
toMapUnit(latitude
);
this.
longitude = Utils.
toMapUnit(longitude
);
}
public int getLatitude
() {
return latitude
;
}
public int getLongitude
() {
return longitude
;
}
public long getId
() {
return 0;
}
public int getHighwayCount
() {
return highwayCount
;
}
public void incHighwayCount
() {
// don't let it wrap
if(highwayCount
< Byte.
MAX_VALUE)
++highwayCount
;
}
public boolean getOnBoundary
() {
return (flags
& ON_BOUNDARY_MASK
) !=
0;
}
public void setOnBoundary
(boolean onBoundary
) {
if (onBoundary
)
this.
flags |= ON_BOUNDARY_MASK
;
else
this.
flags &= ~ON_BOUNDARY_MASK
;
}
public boolean preserved
() {
return (flags
& PRESERVED_MASK
) !=
0;
}
public void preserved
(boolean preserved
) {
if (preserved
)
this.
flags |= PRESERVED_MASK
;
else
this.
flags &= ~PRESERVED_MASK
;
}
/**
* Returns if this coord was marked to be replaced in short arc removal.
* @return True means the replacement has to be looked up.
*/
public boolean isReplaced
() {
return (flags
& REPLACED_MASK
) !=
0;
}
/**
* Mark a point as replaced in short arc removal process.
* @param replaced true or false
*/
public void setReplaced
(boolean replaced
) {
if (replaced
)
this.
flags |= REPLACED_MASK
;
else
this.
flags &= ~REPLACED_MASK
;
}
/**
* Should this Coord be treated like a Garmin node in short arc removal?
* The value has no meaning outside of short arc removal.
* @return true if this coord should be treated like a Garmin node, else false
*/
public boolean isTreatAsNode
() {
return (flags
& TREAT_AS_NODE_MASK
) !=
0;
}
/**
* Mark the Coord to be treated like a Node in short arc removal
* @param treatAsNode true or false
*/
public void setTreatAsNode
(boolean treatAsNode
) {
if (treatAsNode
)
this.
flags |= TREAT_AS_NODE_MASK
;
else
this.
flags &= ~TREAT_AS_NODE_MASK
;
}
/**
* Does this coordinate belong to a node with a fixme tag?
* Note that the value is set after evaluating the points style.
* @return true if the fixme flag is set, else false
*/
public boolean isFixme
() {
return (flags
& FIXME_NODE_MASK
) !=
0;
}
public void setFixme
(boolean b
) {
if (b
)
this.
flags |= FIXME_NODE_MASK
;
else
this.
flags &= ~FIXME_NODE_MASK
;
}
public int hashCode
() {
// Use a factor for latitude to span over the whole integer range:
// max lat: 4194304
// max lon: 8388608
// max hashCode: 2118123520 < 2147483647 (Integer.MAX_VALUE)
return 503 * latitude + longitude
;
}
public boolean equals
(Object obj
) {
if (obj ==
null ||
!(obj
instanceof Coord
))
return false;
Coord other =
(Coord
) obj
;
return latitude == other.
latitude && longitude == other.
longitude;
}
/**
* Distance to other point in meters.
*/
public double distance
(Coord other
) {
return quickDistance
(other
);
}
protected double slowDistance
(Coord other
) {
if (equals
(other
))
return 0;
double lat1 = Utils.
toRadians(latitude
);
double lat2 = Utils.
toRadians(other.
getLatitude());
double lon1 = Utils.
toRadians(getLongitude
());
double lon2 = Utils.
toRadians(other.
getLongitude());
double R =
6371000; // meters
// cosine of great circle angle between points
double cangle =
Math.
sin(lat1
)*Math.
sin(lat2
) +
Math.
cos(lat1
)*Math.
cos(lat2
) * Math.
cos(lon2-lon1
);
return Math.
acos(cangle
) * R
;
}
public double quickDistance
(Coord other
){
return 40075000 * Math.
sqrt(distanceInDegreesSquared
(other
)) /
360;
}
public double distanceInDegreesSquared
(Coord other
) {
if (equals
(other
))
return 0;
double lat1 = Utils.
toDegrees(getLatitude
());
double lat2 = Utils.
toDegrees(other.
getLatitude());
double long1 = Utils.
toDegrees(getLongitude
());
double long2 = Utils.
toDegrees(other.
getLongitude());
double latDiff
;
if (lat1
< lat2
)
latDiff = lat2 - lat1
;
else
latDiff = lat1 - lat2
;
if (latDiff
> 90)
latDiff -=
180;
double longDiff
;
if (long1
< long2
)
longDiff = long2 - long1
;
else
longDiff = long1 - long2
;
if (longDiff
> 180)
longDiff -=
360;
// scale longDiff by cosine of average latitude
longDiff
*=
Math.
cos(Math.
PI /
180 * Math.
abs((lat1 + lat2
) /
2));
return (latDiff
* latDiff
) +
(longDiff
* longDiff
);
}
public Coord makeBetweenPoint
(Coord other,
double fraction
) {
return new Coord
((int)(latitude +
(other.
latitude - latitude
) * fraction
),
(int)(longitude +
(other.
longitude - longitude
) * fraction
));
}
// returns bearing (in degrees) from current point to another point
public double bearingTo
(Coord point
) {
double lat1 = Utils.
toRadians(latitude
);
double lat2 = Utils.
toRadians(point.
latitude);
double lon1 = Utils.
toRadians(longitude
);
double lon2 = Utils.
toRadians(point.
longitude);
double dlon = lon2 - lon1
;
double y =
Math.
sin(dlon
) * Math.
cos(lat2
);
double x =
Math.
cos(lat1
)*Math.
sin(lat2
) -
Math.
sin(lat1
)*Math.
cos(lat2
)*Math.
cos(dlon
);
return Math.
atan2(y, x
) * 180 /
Math.
PI;
}
/**
* Sort lexicographically by longitude, then latitude.
*
* This ordering is used for sorting entries in NOD3.
*/
public int compareTo
(Coord other
) {
if (longitude == other.
getLongitude())
if (latitude == other.
getLatitude()) return 0;
else return latitude
> other.
getLatitude() ? 1 : -
1;
else
return longitude
> other.
getLongitude()? 1: -
1;
}
/**
* Returns a string representation of the object.
*
* @return a string representation of the object.
*/
public String toString
() {
return (latitude
) +
"/" +
(longitude
);
}
public String toDegreeString
() {
Formatter fmt =
new Formatter();
return fmt.
format("%.5f/%.5f",
Utils.
toDegrees(latitude
),
Utils.
toDegrees(longitude
)).
toString();
}
protected String toOSMURL
(int zoom
) {
return ("http://www.openstreetmap.org/?mlat=" +
new Formatter(Locale.
ENGLISH).
format("%.5f", Utils.
toDegrees(latitude
)) +
"&mlon=" +
new Formatter(Locale.
ENGLISH).
format("%.5f", Utils.
toDegrees(longitude
)) +
"&zoom=" +
zoom
);
}
public String toOSMURL
() {
return toOSMURL
(17);
}
}