SQL function to convert UK OS coordinates from easting/northing to longitude and latitude - sql

Please can someone post a SQL function to convert easting/northing to longitude/latitude. I know it's incredibly complicated but I haven't found anyone who has documented it in T-SQL.
This javascript code works but I'm having trouble converting it to SQL.
I have 16,000 coordinates and need them all converted to lat/long.
This is what I have so far but it's not getting past the while loop.
DECLARE #east real = 482353,
#north real = 213371
DECLARE #a real = 6377563.396,
#b real = 6356256.910,
#F0 real = 0.9996012717,
#lat0 real = 49*PI()/180,
#lon0 real = -2*PI()/180
DECLARE #N0 real = -100000,
#E0 real = 400000,
#e2 real = 1 - (#b*#b)/(#a*#a),
#n real = (#a-#b)/(#a+#b)
DECLARE #n2 real = #n*#n,
#n3 real = #n*#n*#n
DECLARE #lat real = #lat0,
#M real = 0
WHILE (#north-#N0-#M >= 0.00001)
BEGIN
SET #lat = ((#north-#N0-#M)/(#a*#F0)) + #lat
DECLARE #Ma real = (1 + #n + (5/4)*#n2 + (5/4)*#n3) * (#lat-#lat0),
#Mb real = (3*#n + 3*#n*#n + (21/8)*#n3) * SIN(#lat-#lat0) * COS(#lat+#lat0),
#Mc real = ((15/8)*#n2 + (15/8)*#n3) * SIN(2*(#lat-#lat0)) * COS(2*(#lat+#lat0)),
#Md real = (35/24)*#n3 * SIN(3*(#lat-#lat0)) * COS(3*(#lat+#lat0))
SET #M = #b * #F0 * (#Ma - #Mb + #Mc - #Md)
END
DECLARE #cosLat real = COS(#lat),
#sinLat real = SIN(#lat)
DECLARE #nu real = #a*#F0/sqrt(1-#e2*#sinLat*#sinLat)
DECLARE #rho real = #a*#F0*(1-#e2)/POWER(1-#e2*#sinLat*#sinLat, 1.5)
DECLARE #eta2 real = #nu/#rho-1
DECLARE #tanLat real = tan(#lat)
DECLARE #tan2lat real = #tanLat*#tanLat
DECLARE #tan4lat real = #tan2lat*#tan2lat
DECLARE #tan6lat real = #tan4lat*#tan2lat
DECLARE #secLat real = 1/#cosLat
DECLARE #nu3 real = #nu*#nu*#nu
DECLARE #nu5 real = #nu3*#nu*#nu
DECLARE #nu7 real = #nu5*#nu*#nu
DECLARE #VII real = #tanLat/(2*#rho*#nu)
DECLARE #VIII real = #tanLat/(24*#rho*#nu3)*(5+3*#tan2lat+#eta2-9*#tan2lat*#eta2)
DECLARE #IX real = #tanLat/(720*#rho*#nu5)*(61+90*#tan2lat+45*#tan4lat)
DECLARE #X real = #secLat/#nu
DECLARE #XI real = #secLat/(6*#nu3)*(#nu/#rho+2*#tan2lat)
DECLARE #XII real = #secLat/(120*#nu5)*(5+28*#tan2lat+24*#tan4lat)
DECLARE #XIIA real = #secLat/(5040*#nu7)*(61+662*#tan2lat+1320*#tan4lat+720*#tan6lat)
DECLARE #dE real = (#east-#E0)
DECLARE #dE2 real = #dE*#dE
DECLARE #dE3 real = #dE2*#dE
DECLARE #dE4 real = #dE2*#dE2,
#dE5 real = #dE3*#dE2
DECLARE #dE6 real = #dE4*#dE2,
#dE7 real = #dE5*#dE2
SET #lat = #lat - #VII*#dE2 + #VIII*#dE4 - #IX*#dE6
DECLARE #lon real = #lon0 + #X*#dE - #XI*#dE3 + #XII*#dE5 - #XIIA*#dE7
SELECT #lon, #lat

I've been struggling with this one for a while.
I had a lot of northing/easting points in OSGB36 that have to be converted on the fly on a regular basis.
Please note that the UDF below converts northings/eastings in OSGB36 (Ordnance Survey) projection to latitude/longitude in WGS84 projection so they can be used in Google Maps.
/****** Object: UserDefinedFunction [dbo].[NEtoLL] Script Date: 09/06/2012 17:06:39 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[NEtoLL] (#East INT, #North INT, #LatOrLng VARCHAR(3)) RETURNS FLOAT AS
BEGIN
--Author: Sandy Motteram
--Date: 06 September 2012
--UDF adapted from javascript at http://www.bdcc.co.uk/LatLngToOSGB.js
--found on page http://mapki.com/wiki/Tools:Snippets
--Instructions:
--Latitude and Longitude are calculated based on BOTH the easting and northing values from the OSGB36
--This UDF takes both easting and northing values in OSGB36 projection and you must specify if a latitude or longitude co-ordinate should be returned.
--IT first converts E/N values to lat and long in OSGB36 projection, then converts those values to lat/lng in WGS84 projection
--Sample values below
--DECLARE #East INT, #North INT, #LatOrLng VARCHAR(3)
--SELECT #East = 529000, #North = 183650 --that combo should be the corner of Camden High St and Delancey St
DECLARE #Pi FLOAT
, #K0 FLOAT
, #OriginLat FLOAT
, #OriginLong FLOAT
, #OriginX FLOAT
, #OriginY FLOAT
, #a FLOAT
, #b FLOAT
, #e2 FLOAT
, #ex FLOAT
, #n1 FLOAT
, #n2 FLOAT
, #n3 FLOAT
, #OriginNorthings FLOAT
, #lat FLOAT
, #lon FLOAT
, #Northing FLOAT
, #Easting FLOAT
SELECT #Pi = 3.14159265358979323846
, #K0 = 0.9996012717 -- grid scale factor on central meridean
, #OriginLat = 49.0
, #OriginLong = -2.0
, #OriginX = 400000 -- 400 kM
, #OriginY = -100000 -- 100 kM
, #a = 6377563.396 -- Airy Spheroid
, #b = 6356256.910
/* , #e2
, #ex
, #n1
, #n2
, #n3
, #OriginNorthings*/
-- compute interim values
SELECT #a = #a * #K0
, #b = #b * #K0
SET #n1 = (#a - #b) / (#a + #b)
SET #n2 = #n1 * #n1
SET #n3 = #n2 * #n1
SET #lat = #OriginLat * #Pi / 180.0 -- to radians
SELECT #e2 = (#a * #a - #b * #b) / (#a * #a) -- first eccentricity
, #ex = (#a * #a - #b * #b) / (#b * #b) -- second eccentricity
SET #OriginNorthings = #b * #lat + #b * (#n1 * (1.0 + 5.0 * #n1 * (1.0 + #n1) / 4.0) * #lat
- 3.0 * #n1 * (1.0 + #n1 * (1.0 + 7.0 * #n1 / 8.0)) * SIN(#lat) * COS(#lat)
+ (15.0 * #n1 * (#n1 + #n2) / 8.0) * SIN(2.0 * #lat) * COS(2.0 * #lat)
- (35.0 * #n3 / 24.0) * SIN(3.0 * #lat) * COS(3.0 * #lat))
SELECT #northing = #north - #OriginY
, #easting = #east - #OriginX
DECLARE #nu FLOAT
, #phid FLOAT
, #phid2 FLOAT
, #t2 FLOAT
, #t FLOAT
, #q2 FLOAT
, #c FLOAT
, #s FLOAT
, #nphid FLOAT
, #dnphid FLOAT
, #nu2 FLOAT
, #nudivrho FLOAT
, #invnurho FLOAT
, #rho FLOAT
, #eta2 FLOAT
/* Evaluate M term: latitude of the northing on the centre meridian */
SET #northing = #northing + #OriginNorthings
SET #phid = #northing / (#b*(1.0 + #n1 + 5.0 * (#n2 + #n3) / 4.0)) - 1.0
SET #phid2 = #phid + 1.0
WHILE (ABS(#phid2 - #phid) > 0.000001)
BEGIN
SET #phid = #phid2;
SET #nphid = #b * #phid + #b * (#n1 * (1.0 + 5.0 * #n1 * (1.0 + #n1) / 4.0) * #phid
- 3.0 * #n1 * (1.0 + #n1 * (1.0 + 7.0 * #n1 / 8.0)) * SIN(#phid) * COS(#phid)
+ (15.0 * #n1 * (#n1 + #n2) / 8.0) * SIN(2.0 * #phid) * COS(2.0 * #phid)
- (35.0 * #n3 / 24.0) * SIN(3.0 * #phid) * COS(3.0 * #phid))
SET #dnphid = #b * ((1.0 + #n1 + 5.0 * (#n2 + #n3) / 4.0) - 3.0 * (#n1 + #n2 + 7.0 * #n3 / 8.0) * COS(2.0 * #phid)
+ (15.0 * (#n2 + #n3) / 4.0) * COS(4 * #phid) - (35.0 * #n3 / 8.0) * COS(6.0 * #phid))
SET #phid2 = #phid - (#nphid - #northing) / #dnphid
END
SELECT #c = COS(#phid)
, #s = SIN(#phid)
, #t = TAN(#phid)
SELECT #t2 = #t * #t
, #q2 = #easting * #easting
SET #nu2 = (#a * #a) / (1.0 - #e2 * #s * #s)
SET #nu = SQRT(#nu2)
SET #nudivrho = #a * #a * #c * #c / (#b * #b) - #c * #c + 1.0
SET #eta2 = #nudivrho - 1
SET #rho = #nu / #nudivrho;
SET #invnurho = ((1.0 - #e2 * #s * #s) * (1.0 - #e2 * #s * #s)) / (#a * #a * (1.0 - #e2))
SET #lat = #phid - #t * #q2 * #invnurho / 2.0 + (#q2 * #q2 * (#t / (24 * #rho * #nu2 * #nu) * (5 + (3 * #t2) + #eta2 - (9 * #t2 * #eta2))))
SET #lon = (#easting / (#c * #nu))
- (#easting * #q2 * ((#nudivrho + 2.0 * #t2) / (6.0 * #nu2)) / (#c * #nu))
+ (#q2 * #q2 * #easting * (5 + (28 * #t2) + (24 * #t2 * #t2)) / (120 * #nu2 * #nu2 * #nu * #c))
SELECT #lat = #lat * 180.0 / #Pi
, #lon = #lon * 180.0 / #Pi + #OriginLong
--Now convert the lat and long from OSGB36 to WGS84
DECLARE #OGlat FLOAT
, #OGlon FLOAT
, #height FLOAT
SELECT #OGlat = #lat
, #OGlon = #lon
, #height = 24 --London's mean height above sea level is 24 metres. Adjust for other locations.
DECLARE #deg2rad FLOAT
, #rad2deg FLOAT
, #radOGlat FLOAT
, #radOGlon FLOAT
SELECT #deg2rad = #Pi / 180
, #rad2deg = 180 / #Pi
--first off convert to radians
SELECT #radOGlat = #OGlat * #deg2rad
, #radOGlon = #OGlon * #deg2rad
--these are the values for WGS84(GRS80) to OSGB36(Airy)
DECLARE #a2 FLOAT
, #h FLOAT
, #xp FLOAT
, #yp FLOAT
, #zp FLOAT
, #xr FLOAT
, #yr FLOAT
, #zr FLOAT
, #sf FLOAT
, #e FLOAT
, #v FLOAT
, #x FLOAT
, #y FLOAT
, #z FLOAT
, #xrot FLOAT
, #yrot FLOAT
, #zrot FLOAT
, #hx FLOAT
, #hy FLOAT
, #hz FLOAT
, #newLon FLOAT
, #newLat FLOAT
, #p FLOAT
, #errvalue FLOAT
, #lat0 FLOAT
SELECT #a2 = 6378137 -- WGS84_AXIS
, #e2 = 0.00669438037928458 -- WGS84_ECCENTRIC
, #h = #height -- height above datum (from $GPGGA sentence)
, #a = 6377563.396 -- OSGB_AXIS
, #e = 0.0066705397616 -- OSGB_ECCENTRIC
, #xp = 446.448
, #yp = -125.157
, #zp = 542.06
, #xr = 0.1502
, #yr = 0.247
, #zr = 0.8421
, #s = -20.4894
-- convert to cartesian; lat, lon are in radians
SET #sf = #s * 0.000001
SET #v = #a / (sqrt(1 - (#e * (SIN(#radOGlat) * SIN(#radOGlat)))))
SET #x = (#v + #h) * COS(#radOGlat) * COS(#radOGlon)
SET #y = (#v + #h) * COS(#radOGlat) * SIN(#radOGlon)
SET #z = ((1 - #e) * #v + #h) * SIN(#radOGlat)
-- transform cartesian
SET #xrot = (#xr / 3600) * #deg2rad
SET #yrot = (#yr / 3600) * #deg2rad
SET #zrot = (#zr / 3600) * #deg2rad
SET #hx = #x + (#x * #sf) - (#y * #zrot) + (#z * #yrot) + #xp
SET #hy = (#x * #zrot) + #y + (#y * #sf) - (#z * #xrot) + #yp
SET #hz = (-1 * #x * #yrot) + (#y * #xrot) + #z + (#z * #sf) + #zp
-- Convert back to lat, lon
SET #newLon = ATAN(#hy / #hx)
SET #p = SQRT((#hx * #hx) + (#hy * #hy))
SET #newLat = ATAN(#hz / (#p * (1 - #e2)))
SET #v = #a2 / (SQRT(1 - #e2 * (SIN(#newLat) * SIN(#newLat))))
SET #errvalue = 1.0;
SET #lat0 = 0
WHILE (#errvalue > 0.001)
BEGIN
SET #lat0 = ATAN((#hz + #e2 * #v * SIN(#newLat)) / #p)
SET #errvalue = ABS(#lat0 - #newLat)
SET #newLat = #lat0
END
--convert back to degrees
SET #newLat = #newLat * #rad2deg
SET #newLon = #newLon * #rad2deg
DECLARE #ReturnMe FLOAT
SET #ReturnMe = 0
IF #LatOrLng = 'Lat'
SET #ReturnMe = #newLat
IF #LatOrLng = 'Lng'
SET #ReturnMe = #newLon
RETURN #ReturnMe
END
GO

I ended up using the following javascript functions to convert the values. I know it's not a SQL solution but it did the job for me.
function OSGridToLatLong(E, N) {
var a = 6377563.396, b = 6356256.910; // Airy 1830 major & minor semi-axes
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
var lat0 = 49*Math.PI/180, lon0 = -2*Math.PI/180; // NatGrid true origin
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
var lat=lat0, M=0;
do {
lat = (N-N0-M)/(a*F0) + lat;
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
} while (N-N0-M >= 0.00001); // ie until < 0.01mm
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
var eta2 = nu/rho-1;
var tanLat = Math.tan(lat);
var tan2lat = tanLat*tanLat, tan4lat = tan2lat*tan2lat, tan6lat = tan4lat*tan2lat;
var secLat = 1/cosLat;
var nu3 = nu*nu*nu, nu5 = nu3*nu*nu, nu7 = nu5*nu*nu;
var VII = tanLat/(2*rho*nu);
var VIII = tanLat/(24*rho*nu3)*(5+3*tan2lat+eta2-9*tan2lat*eta2);
var IX = tanLat/(720*rho*nu5)*(61+90*tan2lat+45*tan4lat);
var X = secLat/nu;
var XI = secLat/(6*nu3)*(nu/rho+2*tan2lat);
var XII = secLat/(120*nu5)*(5+28*tan2lat+24*tan4lat);
var XIIA = secLat/(5040*nu7)*(61+662*tan2lat+1320*tan4lat+720*tan6lat);
var dE = (E-E0), dE2 = dE*dE, dE3 = dE2*dE, dE4 = dE2*dE2, dE5 = dE3*dE2, dE6 = dE4*dE2, dE7 = dE5*dE2;
lat = lat - VII*dE2 + VIII*dE4 - IX*dE6;
var lon = lon0 + X*dE - XI*dE3 + XII*dE5 - XIIA*dE7;
return {
longitude: lon.toDeg(),
latitude: lat.toDeg()
};
}
Number.prototype.toRad = function() { // convert degrees to radians
return this * Math.PI / 180;
}
Number.prototype.toDeg = function() { // convert radians to degrees (signed)
return this * 180 / Math.PI;
}
Number.prototype.padLZ = function(w) {
var n = this.toString();
for (var i=0; i<w-n.length; i++) n = '0' + n;
return n;
}

I needed the same function, and javascript made it difficult to interact with the DB. I have converted your JS to PHP and this could be more useful when updating your database - ie: query table, loop through result set, call function, update table.
function OSGridToLatLong($E, $N) {
$a = 6377563.396;
$b = 6356256.910; // Airy 1830 major & minor semi-axes
$F0 = 0.9996012717; // NatGrid scale factor on central meridian
$lat0 = 49*M_PI/180;
$lon0 = -2*M_PI/180; // NatGrid true origin
$N0 = -100000;
$E0 = 400000; // northing & easting of true origin, metres
$e2 = 1 - ($b*$b)/($a*$a); // eccentricity squared
$n = ($a-$b)/($a+$b);
$n2 = $n*$n;
$n3 = $n*$n*$n;
$lat=$lat0;
$M=0;
do {
$lat = ($N-$N0-$M)/($a*$F0) + $lat;
$Ma = (1 + $n + (5/4)*$n2 + (5/4)*$n3) * ($lat-$lat0);
$Mb = (3*$n + 3*$n*$n + (21/8)*$n3) * sin($lat-$lat0) * cos($lat+$lat0);
$Mc = ((15/8)*$n2 + (15/8)*$n3) * sin(2*($lat-$lat0)) * cos(2*($lat+$lat0));
$Md = (35/24)*$n3 * sin(3*($lat-$lat0)) * cos(3*($lat+$lat0));
$M = $b * $F0 * ($Ma - $Mb + $Mc - $Md); // meridional arc
} while ($N-$N0-$M >= 0.00001); // ie until < 0.01mm
$cosLat = cos($lat);
$sinLat = sin($lat);
$nu = $a*$F0/sqrt(1-$e2*$sinLat*$sinLat); // transverse radius of curvature
$rho = $a*$F0*(1-$e2)/pow(1-$e2*$sinLat*$sinLat, 1.5); // meridional radius of curvature
$eta2 = $nu/$rho-1;
$tanLat = tan($lat);
$tan2lat = $tanLat*$tanLat;
$tan4lat = $tan2lat*$tan2lat;
$tan6lat = $tan4lat*$tan2lat;
$secLat = 1/$cosLat;
$nu3 = $nu*$nu*$nu;
$nu5 = $nu3*$nu*$nu;
$nu7 = $nu5*$nu*$nu;
$VII = $tanLat/(2*$rho*$nu);
$VIII = $tanLat/(24*$rho*$nu3)*(5+3*$tan2lat+$eta2-9*$tan2lat*$eta2);
$IX = $tanLat/(720*$rho*$nu5)*(61+90*$tan2lat+45*$tan4lat);
$X = $secLat/$nu;
$XI = $secLat/(6*$nu3)*($nu/$rho+2*$tan2lat);
$XII = $secLat/(120*$nu5)*(5+28*$tan2lat+24*$tan4lat);
$XIIA = $secLat/(5040*$nu7)*(61+662*$tan2lat+1320*$tan4lat+720*$tan6lat);
$dE = ($E-$E0);
$dE2 = $dE*$dE;
$dE3 = $dE2*$dE;
$dE4 = $dE2*$dE2;
$dE5 = $dE3*$dE2;
$dE6 = $dE4*$dE2;
$dE7 = $dE5*$dE2;
$lat = $lat - $VII*$dE2 + $VIII*$dE4 - $IX*$dE6;
$lon = $lon0 + $X*$dE - $XI*$dE3 + $XII*$dE5 - $XIIA*$dE7;
return array(
'longitude' => $lon * 180 / M_PI,
'latitude' => $lat * 180 / M_PI
);
}

If anyone's interested in non-SQL solution I strongly recommend using this http://www.howtocreate.co.uk/php/gridref.php PHP/JavaScript class.
One important thing to mention here is the library supports Helmert transformation.
PHP
$grutoolbox = Grid_Ref_Utils::toolbox();
$source_coords = Array(54.607720,-6.411990);
//get the ellipsoids that will be used
$Airy_1830 = $grutoolbox->get_ellipsoid('Airy_1830');
$WGS84 = $grutoolbox->get_ellipsoid('WGS84');
$Airy_1830_mod = $grutoolbox->get_ellipsoid('Airy_1830_mod');
//get the transform parameters that will be used
$UK_to_GPS = $grutoolbox->get_transformation('OSGB36_to_WGS84');
$GPS_to_Ireland = $grutoolbox->get_transformation('WGS84_to_Ireland65');
//convert to GPS coordinates
$gps_coords = $grutoolbox->Helmert_transform($source_coords,$Airy_1830,$UK_to_GPS,$WGS84);
//convert to destination coordinates
print $grutoolbox->Helmert_transform($source_coords,$WGS84,$GPS_to_Ireland,$Airy_1830_mod,$grutoolbox->HTML);
JavaScript
var grutoolbox = gridRefUtilsToolbox();
var sourceCoords = [54.607720,-6.411990];
//get the ellipsoids that will be used
var Airy1830 = grutoolbox.getEllipsoid('Airy_1830');
var WGS84 = grutoolbox.getEllipsoid('WGS84');
var Airy1830Mod = grutoolbox.getEllipsoid('Airy_1830_mod');
//get the transform parameters that will be used
var UKToGPS = grutoolbox.getTransformation('OSGB36_to_WGS84');
var GPSToIreland = grutoolbox.getTransformation('WGS84_to_Ireland65');
//convert to GPS coordinates
var gpsCoords = grutoolbox.HelmertTransform(sourceCoords,Airy1830,UKToGPS,WGS84);
//convert to destination coordinates
element.innerHTML = grutoolbox.HelmertTransform(sourceCoords,WGS84,GPSToIreland,Airy1830Mod,grutoolbox.HTML);

I have developed a library in .NET to be called from transact sql Converts WGS84/UTM coordinates to Latitude and Longitude
It does just the opposite, but as it uses CoordinateSharp you can download the code and change it easily to convert from lat/long to wgs84 instead.
You can download it from github:
https://github.com/j-v-garcia/UTM2LATITUDE
usage:
SELECT dbo.UTM2LATITUDE(723399.51,4373328.5,'S',30) AS Latitude, dbo.UTM2LONGITUDE(723399.51,4373328.5,'S',30) AS Longitude
result:
39,4805657453054 -0,402592727245112
<param name="XUTM">pos UTM X</param>
<param name="YUTM">pos UTM Y</param>
<param name="LatBand">Latitude band grid zone designation letter (see http://www.dmap.co.uk/utmworld.htm) </param>
<param name="LongBand">Longitude band grid zone designation number (see http://www.dmap.co.uk/utmworld.htm) </param>

Related

Available options with current script > Table or View needed (SQL Server)

So, I have the current script below, but I need to create this as a view and I know you can't use variable within it. I understand there is the option of creating a stored procedure, but I'm not quite sure how to go about.
Create View vwPUs__cwa as (
Declare #salt varchar (25);
DECLARE #Seed int;
DECLARE #LCV tinyint;
DECLARE #CTime DATETIME;
SET #CTime = GETDATE();
SET #Seed = (DATEPART(hh, #Ctime) * 10000000) + (DATEPART(n, #CTime) * 100000)
+ (DATEPART(s, #CTime) * 1000) + DATEPART(ms, #CTime);
SET #LCV = 1;
SET #Salt = CHAR(ROUND((RAND(#Seed) * 94.0) + 32, 3));
WHILE (#LCV < 25)
BEGIN
SET #Salt = #Salt + CHAR(ROUND((RAND() * 94.0) + 32, 3));
SET #LCV = #LCV + 1;
END;
SELECT dc.id,
sys.Fn_varbintohexsubstring(0, Hashbytes('SHA2_512', #salt + dc.decrypt),
1, 0)
AS SaltyHashbrowns,
dc.firstname,
dc.lastname,
dc.statusname,
dc.processingunit,
dc.processingunitnumber
FROM vwdecrypt_cwa dc
WHERE dc.processingunitnumber IN ( 0201301, 0201302, 0201303, 0201308,
0201309, 0201311, 0201312 )
;
The Prcoedure you need to create will be along the lines of
create procedure PUs__cwa
as
set nocount on
declare #salt varchar (25), #Seed int, #LCV tinyint=1, #CTime datetime=GetDate()
set #Seed = (DatePart(hh, #CTime) * 10000000) + (DatePart(n, #CTime) * 100000)
+ (DatePart(s, #CTime) * 1000) + DatePart(ms, #CTime);
set #salt = Char(Round((Rand(#Seed) * 94.0) + 32, 3));
while (#LCV < 25)
begin
set #salt = #salt + Char(Round((Rand() * 94.0) + 32, 3));
set #LCV += 1;
end;
select dc.id,
sys.fn_varbintohexsubstring(0, HashBytes('SHA2_512', #salt + dc.decrypt), 1, 0) as SaltyHashbrowns,
dc.firstname,
dc.lastname,
dc.statusname,
dc.processingunit,
dc.processingunitnumber
from vwdecrypt_cwa dc
where dc.processingunitnumber in ( 0201301, 0201302, 0201303, 0201308, 0201309, 0201311, 0201312 )
And you run it in tsql with exec PUs__cwa
An inline Table-Valued Function would work here. It is effectively treated by the compiler as a parameterized view.
You cannot place RAND() in a UDF, so we must do this in a stored procedure and pass it through:
CREATE OR ALTER FUNCTION vwPUs__cwa (#salt varchar (25))
RETURNS TABLE
AS RETURN
(
SELECT dc.id,
sys.Fn_varbintohexsubstring(0, Hashbytes('SHA2_512', #salt + dc.decrypt),
1, 0)
AS SaltyHashbrowns,
dc.firstname,
dc.lastname,
dc.statusname,
dc.processingunit,
dc.processingunitnumber
FROM vwdecrypt_cwa dc
WHERE dc.processingunitnumber IN ( 0201301, 0201302, 0201303, 0201308,
0201309, 0201311, 0201312 )
);
GO
CREATE OR ALTER PROCEDURE PUs__cwa
AS
SET NOCOUNT, XACT_ABORT ON;
DECLARE #salt varchar(25);
DECLARE #Seed int = (DATEPART(hh, #Ctime) * 10000000) + (DATEPART(n, #CTime) * 100000) + (DATEPART(s, #CTime) * 1000) + DATEPART(ms, #CTime);
DECLARE #LCV tinyint = 1;
DECLARE #CTime DATETIME = GETDATE();
WHILE (#LCV < 25)
BEGIN
SET #LCV = #LCV + 1;
SET #Salt = CONCAT(#Salt, CHAR(ROUND((RAND() * 94.0) + 32, 3)));
END;
SELECT *
FROM vwPUs__cwa (#salt);
GO
You could also run the salt code in a client application, and pass it through as a parameter to an ad-hoc batch:
SELECT *
FROM vwPUs__cwa (#salt);

Accurately calculate moon phases

For a new project I like to calculate the moon phases. So far I haven't seen any code that does that. I don't want to rely on online-services for this.
I have tried some functions, but they are not 100% reliable. Functions I have tried:
NSInteger r = iYear % 100;
r %= 19;
if (r>9){ r -= 19;}
r = ((r * 11) % 30) + iMonth + iDay;
if (iMonth<3){r += 2;}
r -= ((iYear<2000) ? 4 : 8.3);
r = floor(r+0.5);
other one:
float n = floor(12.37 * (iYear -1900 + ((1.0 * iMonth - 0.5)/12.0)));
float RAD = 3.14159265/180.0;
float t = n / 1236.85;
float t2 = t * t;
float as = 359.2242 + 29.105356 * n;
float am = 306.0253 + 385.816918 * n + 0.010730 * t2;
float xtra = 0.75933 + 1.53058868 * n + ((1.178e-4) - (1.55e-7) * t) * t2;
xtra = xtra + (0.1734 - 3.93e-4 * t) * sin(RAD * as) - 0.4068 * sin(RAD * am);
float i = (xtra > 0.0 ? floor(xtra) : ceil(xtra - 1.0));
float j1 = [self julday:iYear iMonth:iMonth iDay:iDay];
float jd = (2415020 + 28 * n) + i;
jd = fmodf((j1-jd + 30), 30);
and last one
NSInteger thisJD = [self julday:iYear iMonth:iMonth iDay:iDay];
float degToRad = 3.14159265 / 180;
float K0, T, T2, T3, J0, F0, M0, M1, B1, oldJ = 0.0;
K0 = floor((iYear-1900)*12.3685);
T = (iYear-1899.5) / 100;
T2 = T*T; T3 = T*T*T;
J0 = 2415020 + 29*K0;
F0 = 0.0001178*T2 - 0.000000155*T3 + (0.75933 + 0.53058868*K0) - (0.000837*T + 0.000335*T2);
M0 = 360*[self getFrac:((K0*0.08084821133)) + 359.2242 - 0.0000333*T2 - 0.00000347*T3];
M1 = 360*[self getFrac:((K0*0.07171366128)) + 306.0253 + 0.0107306*T2 + 0.00001236*T3];
B1 = 360*[self getFrac:((K0*0.08519585128)) + 21.2964 - (0.0016528*T2) - (0.00000239*T3)];
NSInteger phase = 0;
NSInteger jday = 0;
while (jday < thisJD) {
float F = F0 + 1.530588*phase;
float M5 = (M0 + phase*29.10535608)*degToRad;
float M6 = (M1 + phase*385.81691806)*degToRad;
float B6 = (B1 + phase*390.67050646)*degToRad;
F -= 0.4068*sin(M6) + (0.1734 - 0.000393*T)*sin(M5);
F += 0.0161*sin(2*M6) + 0.0104*sin(2*B6);
F -= 0.0074*sin(M5 - M6) - 0.0051*sin(M5 + M6);
F += 0.0021*sin(2*M5) + 0.0010*sin(2*B6-M6);
F += 0.5 / 1440;
oldJ=jday;
jday = J0 + 28*phase + floor(F);
phase++;
}
float jd = fmodf((thisJD-oldJ), 30);
All are working more and less, but none is really giving the correct dates of full moon for 2017 and 2018.
Does anyone have a function that will calculate the moon phases correctly - also based on time zone?
EDIT:
I only want the function for the Moonphases. SwiftAA offers a lot more and only produces not needed overhead in the app.

Problems with TMinuit

I'm trying to minimize a chi square function in ROOT, this is the FCN code:
void myFCNres( int &npar , double * gin , double &f , double * par , int flag ) {
double chi2 = 0;
for ( int i = 1 ; i <= datasetNHres->GetSize()-2 ; ++i ) {
par[7] = Emin + (Emax-Emin)/(2*N_div) + (i-1)*(Emax-Emin)/N_div;
chi2 += pow( ( integrand->Integral( 1.804 , 8 , par , 1E-15) - datasetNHres->GetBinContent(i)) / datasetNHres->GetBinError(i) ,2);
}
chi2 += pow( (par[0] - p[0]) / sig[0] ,2) +
pow( (par[1] - p[1]) / sig[1] ,2) +
pow( (par[2] - p[2]) / sig[2] ,2) +
pow( (par[3] + p[3]) / sig[3] ,2);
f = chi2;
}
The result of running is that none of the parameters has changed, just like my function does not depend on any of them. Do you see problems with FCN logic or I have to look for the bug elsewhere?
Thanks,
Luigi

calculate angle with three CGpoint

I have three CGpoint and I would like calculate the angle.
I drawn a little schema :
I tried with this code :
CGPoint u ;
u.x = 0;
u.y = - middleRectY;
CGPoint v ;
v.x = x1 - middelRectX;
v.y = y1 - middleRectY;
// formule = u.v / ( ||u|| * ||v||)
double cosa = (double)((u.x * v.x + u.y * v.y)) / sqrt(u.x * u.x + u.y * u.y) * sqrt(v.x * v.x + v.y * v.y);
// angle en degré
double angle = (180.0 / M_PI) * acos(cosa);
// Signe de l'angle
int sign = (u.x * v.y - u.y * v.x) > 0 ? 1 : -1;
rectYellow.transform = CGAffineTransformMakeRotation(angle*sign);
But my function return "nan" :/
Thx :)
I found the problem !
It just a probleme of parenthesis :
double cosa = ((u.x * v.x) + (u.y * v.y)) / (sqrt((u.x * u.x) + (u.y * u.y)) * sqrt((v.x * v.x) + (v.y * v.y)));
I don't understand why ?
Because the parentheses aren't necessary for multiplication ...

Distance using WGS84-ellipsoid

Consider points P1 (60°N, 20°E, 0) and P2 (60°N, 22°E, 0) on the
surface of the Earth
What is the shortest distance between the points P1 and P2, when the shape of the
Earth is modeled using WGS-84 ellipsoid?
Unfortunately, Vincenty's algorithm fails to converge for some inputs.
GeographicLib provides an alternative which always converges (and
is also more accurate). Implementations in C++, C, Fortran, Javascript, Python, Java, and Matlab are provided. E.g., using the
Matlab package:
format long;
geoddistance(60,20,60,22)
->
111595.753650629
As pointed out in a comment to your question, you should use Vincenty's formula for inverse problem.
Answer to your question is: 111595.75 metres (or 60.257 nautical miles).
Javascript implementation of Vincenty's inverse formula, as copied from http://jsperf.com/vincenty-vs-haversine-distance-calculations:
/**
* Calculates geodetic distance between two points specified by latitude/longitude using
* Vincenty inverse formula for ellipsoids
*
* #param {Number} lat1, lon1: first point in decimal degrees
* #param {Number} lat2, lon2: second point in decimal degrees
* #returns (Number} distance in metres between points
*/
function distVincenty(lat1, lon1, lat2, lon2) {
var a = 6378137,
b = 6356752.314245,
f = 1 / 298.257223563; // WGS-84 ellipsoid params
var L = (lon2 - lon1).toRad();
var U1 = Math.atan((1 - f) * Math.tan(lat1.toRad()));
var U2 = Math.atan((1 - f) * Math.tan(lat2.toRad()));
var sinU1 = Math.sin(U1),
cosU1 = Math.cos(U1);
var sinU2 = Math.sin(U2),
cosU2 = Math.cos(U2);
var lambda = L,
lambdaP, iterLimit = 100;
do {
var sinLambda = Math.sin(lambda),
cosLambda = Math.cos(lambda);
var 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
var cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
var sigma = Math.atan2(sinSigma, cosSigma);
var sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
var cosSqAlpha = 1 - sinAlpha * sinAlpha;
var cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
if (isNaN(cos2SigmaM)) cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
var 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)));
} while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);
if (iterLimit == 0) return NaN // formula failed to converge
var uSq = cosSqAlpha * (a * a - b * b) / (b * b);
var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
var s = b * A * (sigma - deltaSigma);
s = s.toFixed(3); // round to 1mm precision
return s;
}
The Haversine Formula is commonly used (error < 0,5%)