I made a java implementation of the new perceptualy uniform color space JzAzBz. OSA publication is : https://www.osapublishing.org/oe/fulltext.cfm?uri=oe-25-13-15131&id=368272.
My java code is :
private double b = 1.15;
private double g = 0.66;
private double c1 = 3424 / Math.pow(2, 12);
private double c2 = 2413 / Math.pow(2, 7);
private double c3 = 2392 / Math.pow(2, 7);
private double n = 2610 / Math.pow(2, 14);
private double p = 1.7 * 2523 / Math.pow(2, 5);
private double d = -0.56;
private double d0 = 1.6295499532821566 * Math.pow(10, -11);
public void XYZToJab(double[] xyz, double[] jab) {
double[] XYZp = new double[3];
XYZp[0] = b * xyz[0] - ((b - 1) * xyz[2]);
XYZp[1] = g * xyz[1] - ((g - 1) * xyz[0]);
XYZp[2] = xyz[2];
double[] LMS = new double[3];
LMS[0] = 0.41478972 * XYZp[0] + 0.579999 * XYZp[1] + 0.0146480 * XYZp[2];
LMS[1] = -0.2015100 * XYZp[0] + 1.120649 * XYZp[1] + 0.0531008 * XYZp[2];
LMS[2] = -0.0166008 * XYZp[0] + 0.264800 * XYZp[1] + 0.6684799 * XYZp[2];
double[] LMSp = new double[3];
for (int i = 0; i < 3; i++) {
LMSp[i] = Math.pow((c1 + c2 * Math.pow((LMS[i] / 10000.0), n)) / (1 + c3 * Math.pow((LMS[i] / 10000.0), n)), p);
}
double[] Iab = new double[3];
Iab[0] = 0.5 * LMSp[0] + 0.5 * LMSp[1];
Iab[1] = 3.524000 * LMSp[0] - 4.066708 * LMSp[1] + 0.542708 * LMSp[2];
Iab[2] = 0.199076 * LMSp[0] + 1.096799 * LMSp[1] - 1.295875 * LMSp[2];
jab[0] = (((1 + d) * Iab[0]) / (1 + d * Iab[0])) - d0;
jab[1] = Iab[1];
jab[2] = Iab[2];
}
public void JabToXYZ(double[] jab, double[] xyz) {
double[] Iab = new double[3];
Iab[0] = (jab[0] + d0) / (1 + d - d * (jab[0] + d0));
Iab[1] = jab[1];
Iab[2] = jab[2];
double[] LMSp = new double[3];
LMSp[0] = 1.0 * Iab[0] + 0.13860504 * Iab[1] + 0.05804732 * Iab[2];
LMSp[1] = 1.0 * Iab[0] - 0.13860504 * Iab[1] - 0.05804732 * Iab[2];
LMSp[2] = 1.0 * Iab[0] - 0.09601924 * Iab[1] - 0.81189190 * Iab[2];
double[] LMS = new double[3];
for (int i = 0; i < 3; i++) {
LMS[i] = 10000 * Math.pow((c1 - Math.pow(LMSp[i], 1 / p)) / ((c3 * Math.pow(LMSp[i], 1 / p)) - c2), 1 / n);
}
double[] XYZp = new double[3];
XYZp[0] = 1.92422644 * LMS[0] - 1.00479231 * LMS[1] + 0.03765140 * LMS[2];
XYZp[1] = 0.35031676 * LMS[0] + 0.72648119 * LMS[1] - 0.06538442 * LMS[2];
XYZp[2] = -0.09098281 * LMS[0] - 0.31272829 * LMS[1] + 1.52276656 * LMS[2];
xyz[0] = (XYZp[0] + (b - 1) * XYZp[2]) / b;
xyz[1] = (XYZp[1] + (g - 1) * XYZp[0]) / g;
xyz[2] = XYZp[2];
}
When I test it running XYZToJab and then JabToXYZ I get a good precision for X and Z (delta order is E-9) but for Y I get a bad precision (delta order is 1-5%).
Is there anyone who can help me ?
The implementation is almost correct: The error lies in JabToXYZ where the prior to last line should be changed from
(XYZp[1] + (g - 1) * XYZp[0]) / g;
to
(XYZp[1] + (g - 1) * xyz[0]) / g;
However the fact that you are using rounded invert matrices to 6 decimal places in the JabToXYZ function will prevent you to get a clean inversion. You should try to compute the inverse at full double precision:
>>> import numpy as np
>>> np.set_printoptions(formatter={'float': '{:0.15f}'.format})
>>> import colour.models.jzazbz
>>> colour.models.jzazbz.JZAZBZ_IZAZBZ_TO_LMS_P_MATRIX
array([[1.000000000000000, 0.138605043271539, 0.058047316156119],
[1.000000000000000, -0.138605043271539, -0.058047316156119],
[1.000000000000000, -0.096019242026319, -0.811891896056039]])
>>> colour.models.jzazbz.JZAZBZ_LMS_TO_XYZ_MATRIX
array([[1.924226435787607, -1.004792312595365, 0.037651404030618],
[0.350316762094999, 0.726481193931655, -0.065384422948085],
[-0.090982810982848, -0.312728290523074, 1.522766561305260]])
Related
I am in the process of taking over a project and noticed that, when the vincenty formulae is used,
it has been written in a unusual way. This is how it's written:
String direct(double distance, double initialBearing, double positionlat, double positionlong) {
// if (this.height != 0) throw new RangeError('point must be on the surface of
// the ellipsoid');
double φ1 = this.toRad(positionlat)/* .toRadians() */, λ1 = this.toRad(positionlong)/* .toRadians() */;
double α1 = this.toRad(initialBearing);
double s = distance;
// allow alternative ellipsoid to be specified
// double ellipsoid = /*this.datum ? this.datum.ellipsoid :*/
// LatLonEllipsoidal.ellipsoids.WGS84;
// const {a, b, f} = ellipsoid;
double a = 6378137;
double b = 6356752.314245;
double f = 1 / 298.257223563;
// double a = ellipsoid;
// double b = ellipsoid;
// double f = ellipsoid;
double sinα1 = Math.sin(α1);
double cosα1 = Math.cos(α1);
double tanU1 = (1 - f) * Math.tan(φ1), cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
double σ1 = Math.atan2(tanU1, cosα1); // σ1 = angular distance on the sphere from the equator to P1
double sinα = cosU1 * sinα1; // α = azimuth of the geodesic at the equator
double cosSqα = 1 - sinα * sinα;
double uSq = cosSqα * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double σ = s / (b * A);
Double sinσ = null, cosσ = null, Δσ = null; // σ = angular distance P� P₂ on the sphere
Double cos2σₘ = null; // σₘ = angular distance on the sphere from the equator to the midpoint of the
// line
Double σʹ = null, iterations = 0d;
do {
cos2σₘ = Math.cos(2 * σ1 + σ);
sinσ = Math.sin(σ);
cosσ = Math.cos(σ);
Δσ = B * sinσ * (cos2σₘ + B / 4 * (cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)
- B / 6 * cos2σₘ * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σₘ * cos2σₘ)));
σʹ = σ;
σ = s / (b * A) + Δσ;
} while (Math.abs(σ - σʹ) > 1e-12 && ++iterations < 100);
if (iterations >= 100) {
//throw new Exception("Vincenty formula failed to converge"); // not possible?
System.err.println("Warning: Vincenty formula failed to converge!");
}
double x = sinU1 * sinσ - cosU1 * cosσ * cosα1;
double φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - f) * Math.sqrt(sinα * sinα + x * x));
double λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1);
double C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα));
double L = λ - (1 - C) * f * sinα * (σ + C * sinσ * (cos2σₘ + C * cosσ * (-1 + 2 * cos2σₘ * cos2σₘ)));
double λ2 = λ1 + L;
double α2 = Math.atan2(sinα, -x);
// const destinationPoint = new
// LatLonEllipsoidal_Vincenty(this.toDeg(φ2)/*.toDegrees()*/,
// this.toDeg(λ2)/*.toDegrees()*/, 0, undefined);
return this.toDeg(φ2) + ";" + this.toDeg(λ2);/*
* { lat: this.toDeg(φ2), lng: this.toDeg(λ2)
*/
/*
* destinationPoint.g point: destinationPoint, finalBearing:
* Dms.wrap360(this.toDeg(α2)/*.toDegrees()
*//*
* ), iterations: iterations,
*/
// };
}
Now the IDE (eclipse) is responding, that it can't handle variable names such as e.g. φ1. Is there a elegant solution to fixing this or do I have to re-write it?
Most probably, the source code was written in the UTF-8 encoding, and your Eclipse is configured to interpret the sources as ANSI, ISO-8859-1, cp1252 or similar.
Then, what you see as φ is in fact the two-byte UTF-8 representation of the greek character phi (φ), interpreted according to ANSI.
Configure Eclipse to expect the UTF-8 encoding (Preferences / General / Workspace / Text file encoding), then it should be able to compile.
This example shows why it is a bad idea even today to use characters outside ASCII in source code.
I've been looking for hours now to find a good way to draw a textured sphere in JOGL. All I need is a point in the right direction or some code that works for someone. So far all I have been able to find is helloTexture (while it works, it is obviously not a sphere), would there be a way to convert this into a sphere or should i try my luck somewhere else?
I wrote this for you, it's untested since I modified it to work with GL_TRIANGLES, try and let me know
radius is the sphere radius, rings are the horizontal slices, sectors the vertical ones
private void createGeometry(float radius, short rings, short sectors) {
float R = 1f / (float)(rings - 1);
float S = 1f / (float)(sectors - 1);
short r, s;
float x, y, z;
points = new float[rings * sectors * 3];
normals = new float[rings * sectors * 3];
texcoords = new float[rings * sectors * 2];
int t = 0, v = 0, n = 0;
for(r = 0; r < rings; r++) {
for(s = 0; s < sectors; s++) {
x = (float) (Math.cos(2 * Math.PI * s * S) * Math.sin(Math.PI * r * R ));
y = (float) Math.sin(-Math.PI / 2 + Math.PI * r * R );
z = (float) (Math.sin(2 * Math.PI * s * S) * Math.sin(Math.PI * r * R ));
texcoords[t++] = s * S;
texcoords[t++] = r * R;
points[v++] = x * radius;
points[v++] = y * radius;
points[v++] = z * radius;
normals[n++] = x;
normals[n++] = y;
normals[n++] = z;
}
}
int counter = 0;
indices = new short[rings * sectors * 6];
for(r = 0; r < rings - 1; r++){
for(s = 0; s < sectors-1; s++) {
indices[counter++] = (short) (r * sectors + s);
indices[counter++] = (short) (r * sectors + (s + 1));
indices[counter++] = (short) ((r + 1) * sectors + (s + 1));
indices[counter++] = (short) ((r + 1) * sectors + (s + 1));
indices[counter++] = (short) (r * sectors + (s + 1));
indices[counter++] = (short) ((r + 1) * sectors + s);
}
}
}
I am almost new to Java programming. I want to implement the following code in MATSim (it is a simulation programme for urban planning purposes). This code is about converting WGS84 (longitude and latitude) to OSGB36 (British National Grid).
https://gist.github.com/schoenobates/3497544#file-osgb-java
The MATSim library has an interface (below) to put the conversion codes in it and use it for visual simulation.
I have already tried many times to deal with errors, but it doesn't work me. Would you please give me some ideas about how I can do this.
package org.matsim.core.utils.geometry;
import org.matsim.api.core.v01.Coord;
/**
* A simple interface to convert coordinates from one coordinate system to
* another one.
*
* #author mrieser
*/
public interface CoordinateTransformation {
/**
* Transforms the given coordinate from one coordinate system to the other.
*
* #param coord The coordinate to transform.
* #return The transformed coordinate.
*/
public Coord transform(Coord coord);
}
Thank you, Somayeh
Ok, the error are here:
I did the below:
It is giving me two syntax errors:
double[] eastANDnorth(double lat, double lon) { for curly parenthesis and comma;
and
return new double(OSGB36N, OSGB36E); for double
package org.matsim.core.utils.geometry.transformations;
import org.matsim.api.core.v01.Coord;
import org.matsim.core.utils.geometry.CoordImpl;
import org.matsim.core.utils.geometry.CoordinateTransformation;
public class WGS84toOSGB36 implements CoordinateTransformation {
#Override
public Coord transform(Coord coord) {
// WGS84 ELLIPSOID
double WGS84_A = 6378137;
double WGS84_B = 6356752.314245;
double WGS84_E2 = ((WGS84_A * WGS84_A) - (WGS84_B * WGS84_B) / (WGS84_A * WGS84_A));
// NstGrid scale factor on central meridian
final double F0 = 0.9996012717;
// Airy 1830 major & minor semi-axes - note .909 not .910
final double AIRY_A = 6377563.396;
final double AIRY_B = 6356256.909;
// NatGrid true origin
final double LAT0 = Math.toRadians(49); //Phi0
final double LON0 = Math.toRadians(-2); //Lamda0
// northing & easting of true origin, metres
final double N0 = -100000;
final double E0 = 400000;
// eccentricity squared
final double E2 = ((AIRY_A * AIRY_A) - (AIRY_B * AIRY_B)) / (AIRY_A *AIRY_A);
final double N = (AIRY_A - AIRY_B) / (AIRY_A + AIRY_B);
final double N2 = N * N;
final double N3 = N * N * N;
final double TX = -446.448;
final double TY = 125.157;
final double TZ = -542.060;
final double RX = Math.toRadians(-0.1502 / 3600);
final double RY = Math.toRadians(-0.2470 / 3600);
final double RZ = Math.toRadians(-0.8421 / 3600);
final double S = 20.4894 / 1e6 + 1;
/*----*/double[] eastANDnorth(double lat, double lon) {/* error in this line */
// -- 1: convert polar to cartesian coordinates (using ellipse 1)
// WGS84 ellipsoid
double sinPhi = Math.sin(lat);
double cosPhi = Math.cos(lat);
double sinLambda = Math.sin(lon);
double cosLambda = Math.cos(lon);
double H = 24.7; // for the moment
double nu = WGS84_A / Math.sqrt(1 - WGS84_E2 * sinPhi * sinPhi);
double x1 = (nu + H) * cosPhi * cosLambda;
double y1 = (nu + H) * cosPhi * sinLambda;
double z1 = ((1-WGS84_E2) * nu + H) * sinPhi;
// -- 2: apply helmert transform using appropriate params
double x2 = TX + x1 * S - y1 * RZ + z1 * RY;
double y2 = TY + x1 * RZ + y1 * S - z1 * RX;
double z2 = TZ - x1 * RY + y1 * RX + z1 * S;
// -- 3: convert cartesian to polar coordinates (using ellipse 2)
double precision = 4 / AIRY_A;
double p = Math.sqrt(x2 * x2 + y2 * y2);
double phi = Math.atan2(z2, p * (1 - E2)), phiP = 2 * Math.PI;
while (Math.abs(phi - phiP) > precision) {
nu = AIRY_A / Math.sqrt(1 - E2 * Math.sin(phi) * Math.sin(phi));
phiP = phi;
phi = Math.atan2(z2 + E2 * nu * Math.sin(phi), p);
}
double lambda = Math.atan2(y2, x2);
// -- 4: now we're in OSGB, get the EN coords
double cosLat = Math.cos(phi), sinLat = Math.sin(phi);
nu = AIRY_A * F0 / Math.sqrt(1 - E2 * sinLat * sinLat);
double rho = AIRY_A * F0 * (1 - E2) / Math.pow(1 - E2 * sinLat * sinLat, 1.5);
double eta2 = nu / rho - 1;
double Ma = (1 + N + (5 / 4) * N2 + (5 / 4) * N3) * (phi - LAT0);
double Mb = (3 * N + 3 * N * N + (21 / 8) * N3) * Math.sin(phi - LAT0) * Math.cos(phi + LAT0);
double Mc = ((15 / 8) * N2 + (15 / 8) * N3) * Math.sin(2 * (phi - LAT0)) * Math.cos(2 * (phi + LAT0));
double Md = (35 / 24) * N3 * Math.sin(3 * (phi - LAT0)) * Math.cos(3 * (phi + LAT0));
double M = AIRY_B * F0 * (Ma - Mb + Mc - Md); // meridional arc
double cos3lat = cosLat * cosLat * cosLat;
double cos5lat = cos3lat * cosLat * cosLat;
double tan2lat = Math.tan(phi) * Math.tan(phi);
double tan4lat = tan2lat * tan2lat;
double I = M + N0;
double II = (nu / 2) * sinLat * cosLat;
double III = (nu / 24) * sinLat * cos3lat * (5 - tan2lat + 9 * eta2);
double IIIA = (nu / 720) * sinLat * cos5lat * (61 - 58 * tan2lat + tan4lat);
double IV = nu * cosLat;
double V = (nu / 6) * cos3lat * (nu / rho - tan2lat);
double VI = (nu / 120) * cos5lat * (5 - 18 * tan2lat + tan4lat + 14 * eta2 - 58 * tan2lat * eta2);
double dLon = lambda - LON0;
double dLon2 = dLon * dLon, dLon3 = dLon2 * dLon, dLon4 = dLon3 * dLon, dLon5 = dLon4 * dLon, dLon6 = dLon5 * dLon;
double OSGB36N = I + II * dLon2 + III * dLon4 + IIIA * dLon6;
double OSGB36E = E0 + IV * dLon + V * dLon3 + VI * dLon5;
/*--------*/return new double(OSGB36N, OSGB36E);/* error in this line */
}
return new CoordImpl(OSGB36N, OSGB36E);
}
}
You are trying to create a array with this expression:
new double(OSGB36N, OSGB36E)
However that's invalid java syntax. You should change it to
new double[]{ OSGB36N, OSGB36E }
Also your're trying to declare a method inside another method, i.e. eastANDnorth inside transform, which is not valid in java.
I want to add direction arrows to my application to make it looks like this. Is there any solutions or advices?
Here is my solution:
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.jettaxi.R;
import java.util.ArrayList;
public class ArrowDirectionsOverlay extends Overlay {
private Context context;
private ArrayList<GeoPoint> directions;
private Bitmap arrowBitmap;
private int maximalPositionX;
private int maximalPositionY;
public ArrowDirectionsOverlay(Context context) {
this.context = context;
directions = new ArrayList<GeoPoint>();
arrowBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.direction);
}
public void addDirectionPoint(GeoPoint destination) {
directions.add(destination);
}
public void clear() {
directions.clear();
}
private double getAngle(GeoPoint center, GeoPoint destination) {
double lat1 = center.getLatitudeE6() / 1000000.;
double lon1 = center.getLongitudeE6() / 1000000.;
double lat2 = destination.getLatitudeE6() / 1000000.;
double lon2 = destination.getLongitudeE6() / 1000000.;
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 :
cosU1cosU2 * sinLambda / sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 :
cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared *
(-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared *
(-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) *
cosSqAlpha *
(4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B * sinSigma * // (6)
(cos2SM + (B / 4.0) *
(cosSigma * (-1.0 + 2.0 * cos2SMSq) -
(B / 6.0) * cos2SM *
(-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SMSq)));
lambda = L +
(1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma *
(cos2SM + C * cosSigma *
(-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
float distance = (float) (b * A * (sigma - deltaSigma));
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
return finalBearing;
}
private void drawArrow(Canvas canvas, MapView mapView, GeoPoint destination) {
Point screenPts = mapView.getProjection().toPixels(destination, null);
int deltaX = mapView.getMapCenter().getLatitudeE6() - destination.getLatitudeE6();
int deltaY = mapView.getMapCenter().getLongitudeE6() - destination.getLongitudeE6();
double angle = getAngle(mapView.getMapCenter(), destination);
double tan = Math.tan(Math.toRadians(angle));
Matrix matrix = new Matrix();
matrix.postRotate((float) angle);
Bitmap rotatedBmp = Bitmap.createBitmap(
arrowBitmap,
0, 0,
arrowBitmap.getWidth(),
arrowBitmap.getHeight(),
matrix,
true
);
int currentPositionX = screenPts.x - (rotatedBmp.getWidth() / 2);
int currentPositionY = screenPts.y - (rotatedBmp.getHeight() / 2);
if ((currentPositionX < 0) || (currentPositionY < 0) ||
(currentPositionX > maximalPositionX) || (currentPositionY > maximalPositionY)) {
int arrowPositionX = (int) (mapView.getWidth() / 2 - Math.signum(deltaX) * mapView.getHeight() / 2 * tan);
arrowPositionX = Math.min(Math.max(arrowPositionX, 0), maximalPositionX);
int arrowSpanX = (int) (mapView.getWidth() / 2.);
int arrowPositionY = (int) (mapView.getHeight() / 2 + Math.signum(deltaY) * arrowSpanX / tan);
arrowPositionY = Math.min(Math.max(arrowPositionY, 0), maximalPositionY);
canvas.drawBitmap(
rotatedBmp,
arrowPositionX,
arrowPositionY,
null
);
}
}
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
maximalPositionX = mapView.getWidth() - arrowBitmap.getWidth();
maximalPositionY = mapView.getHeight() - arrowBitmap.getHeight();
for (GeoPoint geoPoint : directions) {
drawArrow(canvas, mapView, geoPoint);
}
}
}
Bitmap arrow points top in my case
My solution would be to do a search for the places of interest (in this case coffee shops) which are within a certain distance from the users current location. This distance could be a 'screen' north, south, east or west of your current location - the screen distance will depend on your zoom level. The shops which are not on the current screen would then be represented by an arrow placed on the screen edge with the direction set between your current location and the shop.
What library/header/class is equivalent to the Java Math class?
Background & bonus question:
I'm trying to port this function to Objective-C from Java. Should I rewrite it, or can I copy and paste and rewrite only the parts that are syntactically different? (In other words, will the Java behave the same way if it were run as Objective-C or C?)
Here's the monster function in Java. It essentially is the Vincinty Formula:
private double vincentyFormula(GeoLocation location, int formula) {
double a = 6378137;
double b = 6356752.3142;
double f = 1 / 298.257223563; // WGS-84 ellipsiod
double L = Math.toRadians(location.getLongitude() - getLongitude());
double U1 = Math
.atan((1 - f) * Math.tan(Math.toRadians(getLatitude())));
double U2 = Math.atan((1 - f)
* Math.tan(Math.toRadians(location.getLatitude())));
double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
double lambda = L;
double lambdaP = 2 * Math.PI;
double iterLimit = 20;
double sinLambda = 0;
double cosLambda = 0;
double sinSigma = 0;
double cosSigma = 0;
double sigma = 0;
double sinAlpha = 0;
double cosSqAlpha = 0;
double cos2SigmaM = 0;
double C;
while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0) {
sinLambda = Math.sin(lambda);
cosLambda = Math.cos(lambda);
sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda)
+ (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)
* (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
if (sinSigma == 0)
return 0; // co-incident points
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
sigma = Math.atan2(sinSigma, cosSigma);
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
cosSqAlpha = 1 - sinAlpha * sinAlpha;
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
if (Double.isNaN(cos2SigmaM))
cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (ß6)
C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
lambdaP = lambda;
lambda = L
+ (1 - C)
* f
* sinAlpha
* (sigma + C
* sinSigma
* (cos2SigmaM + C * cosSigma
* (-1 + 2 * cos2SigmaM * cos2SigmaM)));
}
if (iterLimit == 0)
return Double.NaN; // formula failed to converge
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384
* (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
double deltaSigma = B
* sinSigma
* (cos2SigmaM + B
/ 4
* (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B
/ 6 * cos2SigmaM
* (-3 + 4 * sinSigma * sinSigma)
* (-3 + 4 * cos2SigmaM * cos2SigmaM)));
double distance = b * A * (sigma - deltaSigma);
// initial bearing
double fwdAz = Math.toDegrees(Math.atan2(cosU2 * sinLambda, cosU1
* sinU2 - sinU1 * cosU2 * cosLambda));
// final bearing
double revAz = Math.toDegrees(Math.atan2(cosU1 * sinLambda, -sinU1
* cosU2 + cosU1 * sinU2 * cosLambda));
if (formula == DISTANCE) {
return distance;
} else if (formula == INITIAL_BEARING) {
return fwdAz;
} else if (formula == FINAL_BEARING) {
return revAz;
} else { // should never happpen
return Double.NaN;
}
}
Java's Math class in very much based on the standard C math.h, so you could use that one.
Looks like you're reimplementing existing code. See MKMetersBetweenMapPoints().