static isea_geo isea_ctran(ref isea_geo np, isea_geo pt, double lon0) { isea_geo npt; np.lon += Math.PI; npt = snyder_ctran(np, pt); np.lon -= Math.PI; npt.lon -= (Math.PI - lon0 + np.lon); // snyder is down tri 3, isea is along side of tri1 from vertex 0 to // vertex 1 these are 180 degrees apart npt.lon += Math.PI; // normalize longitude npt.lon = Math.IEEERemainder(npt.lon, 2 * Math.PI); while (npt.lon > Math.PI) { npt.lon -= 2 * Math.PI; } while (npt.lon < -Math.PI) { npt.lon += 2 * Math.PI; } return(npt); }
static double az_adjustment(int triangle) { double adj; isea_geo v = vertex[tri_v1[triangle]]; isea_geo c = icostriangles[triangle]; // TODO looks like the adjustment is always either 0 or 180 // at least if you pick your vertex carefully adj = Math.Atan2(Math.Cos(v.lat) * Math.Sin(v.lon - c.lon), Math.Cos(c.lat) * Math.Sin(v.lat) - Math.Sin(c.lat) * Math.Cos(v.lat) * Math.Cos(v.lon - c.lon)); return(adj); }
static isea_pt isea_forward(isea_dgg g, isea_geo @in) { isea_pt @out, coord; int tri = isea_transform(ref g, @in, out @out); if (g.output == isea_address_form.ISEA_PLANE) { isea_tri_plane(tri, ref @out, g.radius); return(@out); } // convert to isea standard triangle size @out.x = @out.x / g.radius * ISEA_SCALE; @out.y = @out.y / g.radius * ISEA_SCALE; @out.x += 0.5; @out.y += 2.0 * .14433756729740644112; switch (g.output) { case isea_address_form.ISEA_PROJTRI: // nothing to do, already in projected triangle break; case isea_address_form.ISEA_VERTEX2DD: g.quad = isea_ptdd(tri, ref @out); break; case isea_address_form.ISEA_Q2DD: // Same as above, we just don't print as much g.quad = isea_ptdd(tri, ref @out); break; case isea_address_form.ISEA_Q2DI: g.quad = isea_ptdi(ref g, tri, @out, out coord); return(coord); case isea_address_form.ISEA_SEQNUM: isea_ptdi(ref g, tri, @out, out coord); // disn will set g->serial isea_disn(ref g, g.quad, coord); return(coord); case isea_address_form.ISEA_HEX: isea_hex(ref g, tri, @out, out coord); return(coord); } return(@out); }
static int isea_transform(ref isea_dgg g, isea_geo @in, out isea_pt @out) { isea_geo pole; pole.lat = g.o_lat; pole.lon = g.o_lon; isea_geo i = isea_ctran(ref pole, @in, g.o_az); int tri = isea_snyder_forward(i, out @out); @out.x *= g.radius; @out.y *= g.radius; g.triangle = tri; return(tri); }
// formula from Snyder, Map Projections: A working manual, p31 */ // // old north pole at np in new coordinates // could be simplified a bit with fewer intermediates // // TODO take a result pointer static isea_geo snyder_ctran(isea_geo np, isea_geo pt) { double phi = pt.lat; double lambda = pt.lon; double alpha = np.lat; double beta = np.lon; double lambda0 = beta; double cos_p = Math.Cos(phi); double sin_a = Math.Sin(alpha); // mpawm 5-7 double sin_phip = sin_a * Math.Sin(phi) - Math.Cos(alpha) * cos_p * Math.Cos(lambda - lambda0); // mpawm 5-8b // lambda prime minus beta // use the two argument form so we end up in the right quadrant double lp_b = Math.Atan2(cos_p * Math.Sin(lambda - lambda0), (sin_a * cos_p * Math.Cos(lambda - lambda0) + Math.Cos(alpha) * Math.Sin(phi))); double lambdap = lp_b + beta; // normalize longitude // TODO can we just do a modulus ? lambdap = Math.IEEERemainder(lambdap, 2 * Math.PI); while (lambdap > Math.PI) { lambdap -= 2 * Math.PI; } while (lambdap < -Math.PI) { lambdap += 2 * Math.PI; } double phip = Math.Asin(sin_phip); isea_geo npt; npt.lat = phip; npt.lon = lambdap; return(npt); }
// formula from Snyder, Map Projections: A working manual, p31 */ // // old north pole at np in new coordinates // could be simplified a bit with fewer intermediates // // TODO take a result pointer static isea_geo snyder_ctran(isea_geo np, isea_geo pt) { double phi=pt.lat; double lambda=pt.lon; double alpha=np.lat; double beta=np.lon; double lambda0=beta; double cos_p=Math.Cos(phi); double sin_a=Math.Sin(alpha); // mpawm 5-7 double sin_phip=sin_a*Math.Sin(phi)-Math.Cos(alpha)*cos_p*Math.Cos(lambda-lambda0); // mpawm 5-8b // lambda prime minus beta // use the two argument form so we end up in the right quadrant double lp_b=Math.Atan2(cos_p*Math.Sin(lambda-lambda0), (sin_a*cos_p*Math.Cos(lambda-lambda0)+Math.Cos(alpha)*Math.Sin(phi))); double lambdap=lp_b+beta; // normalize longitude // TODO can we just do a modulus ? lambdap=Math.IEEERemainder(lambdap, 2*Math.PI); while(lambdap>Math.PI) lambdap-=2*Math.PI; while(lambdap<-Math.PI) lambdap+=2*Math.PI; double phip=Math.Asin(sin_phip); isea_geo npt; npt.lat=phip; npt.lon=lambdap; return npt; }
static int isea_transform(ref isea_dgg g, isea_geo @in, out isea_pt @out) { isea_geo pole; pole.lat=g.o_lat; pole.lon=g.o_lon; isea_geo i=isea_ctran(ref pole, @in, g.o_az); int tri=isea_snyder_forward(i, out @out); @out.x*=g.radius; @out.y*=g.radius; g.triangle=tri; return tri; }
// coord needs to be in radians static int isea_snyder_forward(isea_geo ll, out isea_pt @out) { // TODO by locality of reference, start by trying the same triangle // as last time // TODO put these constants in as radians to begin with snyder_constants c=constants[(int)snyder_polyhedron.SNYDER_POLY_ICOSAHEDRON]; // plane angle between radius vector to center and adjacent edge of // plane polygon double theta=c.theta*DEG2RAD; // spherical distance from center of polygon face to any of its // vertexes on the globe double g=c.g*DEG2RAD; // spherical angle between radius vector to center and adjacent edge // of spherical polygon on the globe double G=c.G*DEG2RAD; for(int i=1; i<=20; i++) { double z; isea_geo center; center=icostriangles[i]; // step 1 z=Math.Acos(Math.Sin(center.lat)*Math.Sin(ll.lat)+Math.Cos(center.lat)*Math.Cos(ll.lat)*Math.Cos(ll.lon-center.lon)); // not on this triangle if(z>g+0.000005) continue; // TODO DBL_EPSILON double Az=sph_azimuth(center.lon, center.lat, ll.lon, ll.lat); // step 2 // This calculates "some" vertex coordinate double az_offset=az_adjustment(i); Az-=az_offset; // TODO I don't know why we do this. It's not in snyder // maybe because we should have picked a better vertex if(Az<0.0) Az+=2.0*Math.PI; // adjust Az for the point to fall within the range of 0 to // 2(90 - theta) or 60 degrees for the hexagon, by // and therefore 120 degrees for the triangle // of the icosahedron // subtracting or adding multiples of 60 degrees to Az and // recording the amount of adjustment int Az_adjust_multiples=0; // how many multiples of 60 degrees we adjust the azimuth while(Az<0.0) { Az+=DEG120; Az_adjust_multiples--; } while(Az>DEG120+double.Epsilon) { Az-=DEG120; Az_adjust_multiples++; } // step 3 double cot_theta=1.0/Math.Tan(theta); double tan_g=Math.Tan(g); // TODO this is a constant // Calculate q from eq 9. // TODO cot_theta is cot(30) double q=Math.Atan2(tan_g, Math.Cos(Az)+Math.Sin(Az)*cot_theta); // not in this triangle if(z>q+0.000005) continue; // step 4 // Apply equations 5-8 and 10-12 in order // eq 5 // Rprime = 0.9449322893 * R; // R' in the paper is for the truncated double Rprime=0.91038328153090290025; // eq 6 double H=Math.Acos(Math.Sin(Az)*Math.Sin(G)*Math.Cos(g)-Math.Cos(Az)*Math.Cos(G)); // eq 7 // Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; double Ag=Az+G+H-DEG180; // eq 8 double Azprime=Math.Atan2(2.0*Ag, Rprime*Rprime*tan_g*tan_g-2.0*Ag*cot_theta); // eq 10 // cot(theta) = 1.73205080756887729355 double dprime=Rprime*tan_g/(Math.Cos(Azprime)+Math.Sin(Azprime)*cot_theta); // eq 11 double f=dprime/(2.0*Rprime*Math.Sin(q/2.0)); // eq 12 double rho=2.0*Rprime*f*Math.Sin(z/2.0); // add back the same 60 degree multiple adjustment from step // 2 to Azprime Azprime+=DEG120*Az_adjust_multiples; // calculate rectangular coordinates double x=rho*Math.Sin(Azprime); double y=rho*Math.Cos(Azprime); // TODO // translate coordinates to the origin for the particular // hexagon on the flattened polyhedral map plot @out.x=x; @out.y=y; return i; } // should be impossible, this implies that the coordinate is not on // any triangle //fprintf(stderr, "impossible transform: %f %f is not on any triangle\n", // ll.lon*RAD2DEG, ll.lat*RAD2DEG); throw new Exception(); }
static isea_pt isea_forward(isea_dgg g, isea_geo @in) { isea_pt @out, coord; int tri=isea_transform(ref g, @in, out @out); if(g.output==isea_address_form.ISEA_PLANE) { isea_tri_plane(tri, ref @out, g.radius); return @out; } // convert to isea standard triangle size @[email protected]/g.radius*ISEA_SCALE; @[email protected]/g.radius*ISEA_SCALE; @out.x+=0.5; @out.y+=2.0*.14433756729740644112; switch(g.output) { case isea_address_form.ISEA_PROJTRI: // nothing to do, already in projected triangle break; case isea_address_form.ISEA_VERTEX2DD: g.quad=isea_ptdd(tri, ref @out); break; case isea_address_form.ISEA_Q2DD: // Same as above, we just don't print as much g.quad=isea_ptdd(tri, ref @out); break; case isea_address_form.ISEA_Q2DI: g.quad=isea_ptdi(ref g, tri, @out, out coord); return coord; case isea_address_form.ISEA_SEQNUM: isea_ptdi(ref g, tri, @out, out coord); // disn will set g->serial isea_disn(ref g, g.quad, coord); return coord; case isea_address_form.ISEA_HEX: isea_hex(ref g, tri, @out, out coord); return coord; } return @out; }
static isea_geo isea_ctran(ref isea_geo np, isea_geo pt, double lon0) { isea_geo npt; np.lon+=Math.PI; npt=snyder_ctran(np, pt); np.lon-=Math.PI; npt.lon-=(Math.PI-lon0+np.lon); // snyder is down tri 3, isea is along side of tri1 from vertex 0 to // vertex 1 these are 180 degrees apart npt.lon+=Math.PI; // normalize longitude npt.lon=Math.IEEERemainder(npt.lon, 2*Math.PI); while(npt.lon>Math.PI) npt.lon-=2*Math.PI; while(npt.lon<-Math.PI) npt.lon+=2*Math.PI; return npt; }
// coord needs to be in radians static int isea_snyder_forward(isea_geo ll, out isea_pt @out) { // TODO by locality of reference, start by trying the same triangle // as last time // TODO put these constants in as radians to begin with snyder_constants c = constants[(int)snyder_polyhedron.SNYDER_POLY_ICOSAHEDRON]; // plane angle between radius vector to center and adjacent edge of // plane polygon double theta = c.theta * DEG2RAD; // spherical distance from center of polygon face to any of its // vertexes on the globe double g = c.g * DEG2RAD; // spherical angle between radius vector to center and adjacent edge // of spherical polygon on the globe double G = c.G * DEG2RAD; for (int i = 1; i <= 20; i++) { double z; isea_geo center; center = icostriangles[i]; // step 1 z = Math.Acos(Math.Sin(center.lat) * Math.Sin(ll.lat) + Math.Cos(center.lat) * Math.Cos(ll.lat) * Math.Cos(ll.lon - center.lon)); // not on this triangle if (z > g + 0.000005) { continue; // TODO DBL_EPSILON } double Az = sph_azimuth(center.lon, center.lat, ll.lon, ll.lat); // step 2 // This calculates "some" vertex coordinate double az_offset = az_adjustment(i); Az -= az_offset; // TODO I don't know why we do this. It's not in snyder // maybe because we should have picked a better vertex if (Az < 0.0) { Az += 2.0 * Math.PI; } // adjust Az for the point to fall within the range of 0 to // 2(90 - theta) or 60 degrees for the hexagon, by // and therefore 120 degrees for the triangle // of the icosahedron // subtracting or adding multiples of 60 degrees to Az and // recording the amount of adjustment int Az_adjust_multiples = 0; // how many multiples of 60 degrees we adjust the azimuth while (Az < 0.0) { Az += DEG120; Az_adjust_multiples--; } while (Az > DEG120 + double.Epsilon) { Az -= DEG120; Az_adjust_multiples++; } // step 3 double cot_theta = 1.0 / Math.Tan(theta); double tan_g = Math.Tan(g); // TODO this is a constant // Calculate q from eq 9. // TODO cot_theta is cot(30) double q = Math.Atan2(tan_g, Math.Cos(Az) + Math.Sin(Az) * cot_theta); // not in this triangle if (z > q + 0.000005) { continue; } // step 4 // Apply equations 5-8 and 10-12 in order // eq 5 // Rprime = 0.9449322893 * R; // R' in the paper is for the truncated double Rprime = 0.91038328153090290025; // eq 6 double H = Math.Acos(Math.Sin(Az) * Math.Sin(G) * Math.Cos(g) - Math.Cos(Az) * Math.Cos(G)); // eq 7 // Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; double Ag = Az + G + H - DEG180; // eq 8 double Azprime = Math.Atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta); // eq 10 // cot(theta) = 1.73205080756887729355 double dprime = Rprime * tan_g / (Math.Cos(Azprime) + Math.Sin(Azprime) * cot_theta); // eq 11 double f = dprime / (2.0 * Rprime * Math.Sin(q / 2.0)); // eq 12 double rho = 2.0 * Rprime * f * Math.Sin(z / 2.0); // add back the same 60 degree multiple adjustment from step // 2 to Azprime Azprime += DEG120 * Az_adjust_multiples; // calculate rectangular coordinates double x = rho * Math.Sin(Azprime); double y = rho * Math.Cos(Azprime); // TODO // translate coordinates to the origin for the particular // hexagon on the flattened polyhedral map plot @out.x = x; @out.y = y; return(i); } // should be impossible, this implies that the coordinate is not on // any triangle //fprintf(stderr, "impossible transform: %f %f is not on any triangle\n", // ll.lon*RAD2DEG, ll.lat*RAD2DEG); throw new Exception(); }