diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..417f7d39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + + +# ========================= +# Editor Temp Files +# ========================= + +.vs/ diff --git a/index.html b/index.html index 5fc64660..8ae53f3b 100644 --- a/index.html +++ b/index.html @@ -1078,6 +1078,8 @@
.map
+
.osm
+
.svg
.png
@@ -2006,6 +2008,7 @@ + @@ -2032,6 +2035,7 @@ + diff --git a/libs/NDSFutility.js b/libs/NDSFutility.js new file mode 100644 index 00000000..f2ff3888 --- /dev/null +++ b/libs/NDSFutility.js @@ -0,0 +1,1164 @@ +/* NDSFutility.js JavaScript functions for NDSF coordinate conversion utility */ + +/* most conversion functions were converted from C fucntions provided by + Dana Yoerger, Steve Gegg, and Louis Whitcomb at WHOI*/ + +CMS_INCH = 2.54; /* cms/inch */ +METERS_FOOT = 0.3048; /* meters/foot */ +METERS_NMILE = 1852.0; /* meters/nautical mile */ +METERS_FATHOM = 1.8288; /* meters/fathom (straight conversion) */ +UMETERS_UFATHOM = 1.8750; /* unc mtrs/unc fthm - accounts for */ + /* snd vel of 1500m/sec to 800fm/sec */ +PI = 3.1415927; +TWO_PI = 6.2831854; +RTOD =57.29577951308232; +DTOR =(1.0/RTOD); +RADIANS = 57.2957795; /* degrees/radian */ +SECS_HOUR = 3600; +/* coordinate translation options */ +XY_TO_LL= 1; +LL_TO_XY= 2; +LL_TO_LL= 3; + +/* coordinate system types */ +GPS_COORDS_WGS84 = 1; +GPS_COORDS_C1866 = 2; +LORANC_COORDS = 3; +SURVEY_COORDS = 4; +TRANSIT_COORDS = 5; + +RADIUS = 6378137.0; +FLATTENING = 0.00335281068; /* GRS80 or WGS84 */ +K_NOT = 0.9996; /* UTM scale factor */ +DEGREES_TO_RADIANS = 0.01745329252; +FALSE_EASTING = 500000.0; +FALSE_NORTHING = 10000000.0; + +function checkForm(form) +{ + if (form["from"].value =="") + { + alert("Please select 'from'."); + return false; + } + if (form["to"].value == "") + { + alert("Please select 'to'."); + return false; + } + return true; +} + + +function ChangeUNS(Form) +{ + if( Form.UNS.value == "N" ) + { + Form.UNS.value = "S"; + } + else + { + Form.UNS.value = "N"; + } +} + +function ChangeNS(Form) +{ + if( Form.NS.value == "N" ) + { + Form.NS.value = "S"; + } + else + { + Form.NS.value = "N"; + } +} + +function ChangeEW(Form) +{ + if( Form.EW.value == "W" ) + { + Form.EW.value = "E"; + } + else + { + Form.EW.value = "W"; + } +} + +function ChangeSNS(Form) +{ + if( Form.SNS.value=="N" ) + { + Form.SNS.value="S"; + } + else + { + Form.SNS.value="N"; + } +} + +function ChangeSEW(Form) +{ + if( Form.SEW.value=="W" ) + { + Form.SEW.value="E"; + } + else + { + Form.SEW.value="W"; + } +} + +function ClearXY(Form) +{ + /* when changing input, clear the results first*/ + Form.Xcord.value=""; Form.Ycord.value=""; +} + +function ClearLL(Form) +{ + /* when changing input, clear the results first*/ + Form.DecSLat.value=""; + Form.SLatDeg.value=""; + Form.SLatMin.value=""; + Form.SNS.value=""; + + Form.DecSLon.value=""; + Form.SLonDeg.value=""; + Form.SLonMin.value=""; + Form.SEW.value=""; +} + +function ClearLatDM(Form) +{ + Form.SLatDeg.value=""; + Form.SLatMin.value=""; +} + +function ClearLonDM(Form) +{ + + Form.SLonDeg.value=""; + Form.SLonMin.value=""; +} + +function ClearLatDecDeg(Form) +{ + Form.DecSLat.value=""; +} + +function ClearLonDecDeg(Form) +{ + Form.DecSLon.value=""; +} + +function ClearOLatDM(Form) +{ + Form.LatDeg.value=""; + Form.LatMin.value=""; +} + +function ClearOLonDM(Form) +{ + Form.LonDeg.value=""; + Form.LonMin.value=""; +} + +function ClearOLatDecDeg(Form) +{ + Form.DecLat.value=""; +} + +function ClearOLonDecDeg(Form) +{ + Form.DecLon.value=""; +} + +function ClearUTM(Form) +{ + /* when changing input, clear the results first*/ + Form.UTMX.value=""; Form.UTMY.value=""; + if ( Form.name == "LL2UTMForm" ) Form.UTMZone.value=""; + if ( Form.name == "XY2UTMForm" ) Form.UTMZone.value=""; +} + +function DEG_TO_RADIANS(x) +{ + return (x/RADIANS); +} + +function RAD_TO_DEGREES(x) +{ + return (x*RADIANS); /* radians (a) to degrees */ +} + +function DEGMIN_TO_DECDEG(x,y) +{ + return (x + y/60.0); /* deg,min to decimal degrees */ +} + +function DEGMIN_TO_SECS(x,y) +{ + return ((x*3600.0)+(y*60.0)); /* deg,min to seconds */ + +} + +function MSEC_TO_KNOTS(x) +{ + return ((x/METERS_NMILE)*SECS_HOUR); +} + +function KNOTS_TO_MSEC(x) +{ + return ((x*METERS_NMILE)/SECS_HOUR); +} + +function FEET_TO_METERS(x) +{ + return (X * METERS_FOOT); +} + +function deg_min_2_deg(deg,dec_min) +{ + /* convert deg and min to decimal degrees */ + dec_deg = deg*1 + (dec_min/60.0); + + return (dec_deg); +} + +function deg_min_sec_2_deg(deg,min,sec) +{ + dec_deg = deg*1.0 + (min/60.0) +(sec/3600.0); + return (dec_deg); +} + + +function deg_2_deg_min (x) +{ + with(Math) + { + whole_deg = floor(abs(x))*((x > 0.0) ? 1.0 : -1.0); + dec_min = (60.0*(x - (whole_deg))); + + if (dec_min.toFixed(4) >=60) + { + dec_min=0; + whole_deg=whole_deg + 1; + } + dm={}; + dm={deg:whole_deg,min:dec_min}; + return(dm); + } +} + +function METERS_DEGLON(x) +{ + with (Math) + { + var d2r=DEG_TO_RADIANS(x); + return((111415.13 * cos(d2r))- (94.55 * cos(3.0*d2r)) + (0.12 * cos(5.0*d2r))); + } +} + +function METERS_DEGLAT(x) +{ + with (Math) + { + var d2r=DEG_TO_RADIANS(x); + return(111132.09 - (566.05 * cos(2.0*d2r))+ (1.20 * cos(4.0*d2r)) - (0.002 * cos(6.0*d2r))); + } +} + +/*---------------------------------------------------------- +# The following functions are modified from the origin +# C functions created by Louis Whitcomb 19 Jun 2001 +# Fixed translate_coordinates by TeaWithLucas 2019 + ---------------------------------------------------------*/ +/*---------------------------------------------------------- +# translate_coordinates +# routine to translate between geographic and cartesian coordinates +# user must supply following data on the cartesian coordinate system: +# location of the origin in lat/lon degrees; +# rotational skew from true north in degrees; +# N.B. sense of rotation i/p here is exactly as o/p by ORIGIN +# x/y offset in meters - only if an offset was used during the +# running of prog ORIGIN; +*/ + +function translate_coordinates(trans_option,porg) { + with(Math) { + var xx,yy,r,ct,st,angle; + angle = DEG_TO_RADIANS(porg.rotation_angle_degs); + if( trans_option == XY_TO_LL) { + /* X,Y to Lat/Lon Coordinate Translation */ + pxpos_mtrs = porg.x; + pypos_mtrs = porg.y; + xx = pxpos_mtrs - porg.xoffset_mtrs; + yy = pypos_mtrs - porg.yoffset_mtrs; + r = sqrt(xx*xx + yy*yy); + + if(r) { + ct = xx/r; + st = yy/r; + xx = r * ( (ct * cos(angle))+ (st * sin(angle)) ); + yy = r * ( (st * cos(angle))- (ct * sin(angle)) ); + } + + var plon = porg.olon + xx/METERS_DEGLON(porg.olat); + var plat = porg.olat + yy/METERS_DEGLAT(porg.olat); + + //if plon or plat are above max values, find where it would be + while(plon < -180){ + plon +=360; + } + while (plon > 180){ + plon -= 360; + } + while(plat < -90){ + plat +=180; + } + while (plat > 90){ + plat -= 180; + } + + var sll={}; + sll={slat:plat, slon:plon}; + + return(sll); + + } else if(trans_option == LL_TO_XY) { + + xx = (porg.slon - porg.olon)*METERS_DEGLON(porg.olat); + yy = (porg.slat - porg.olat)*METERS_DEGLAT(porg.olat); + + r = sqrt(xx*xx + yy*yy); + + /* alert('LL_TO_XY: xx=' + xx + ' yy=' + yy + ' r=' + r); + return false;*/ + + if(r){ + ct = xx/r; + st = yy/r; + xx = r * ( (ct * cos(angle)) + (st * sin(angle)) ); + yy = r * ( (st * cos(angle)) - (ct * sin(angle)) ); + } + + pxpos_mtrs = xx + porg.xoffset_mtrs; + pypos_mtrs = yy + porg.yoffset_mtrs; + + var sxy={}; + sxy={x:pxpos_mtrs, y:pypos_mtrs}; + + return(sxy); + } + } +} +/*-------------------------------------------------*/ + +function utm_zone(slat, slon) +{ + with(Math) + { + /* determine the zone for the given longitude + with 6 deg wide longitudinal strips */ + + var zlon= slon + 180; /* set the lon from 0-360 */ + + for (var i=1; i<=60; i++) + { + if ( zlon >= (i-1)*6 & zlon < i*6) + { + break; + } + } + var zone=i; + + /* modify the zone number for special areas */ + if ( slat >=72 & (slon >=0 & slon <=36)) + { + if (slon < 9.0) + { + zone= 31; + } + else if ( slon >= 9.0 & slon < 21) + { + zone= 33; + } + else if ( slon >= 21.0 & slon < 33) + { + zone= 35; + } + else if ( slon >= 33.0 & slon < 42) + { + zone= 37; + } + } + if ( (slat >=56 & slat < 64) & (slon >=3 & slon < 12)) + { + zone= 32; /* extent to west ward for 3deg more */ + } + return (zone); + } + return true; +} + +/*-------------------------------------------------*/ + +function geo_utm(lat, lon, zone) +{ + with(Math) + { + /* first compute the necessary geodetic parameters and constants */ + + lambda_not = ((-180.0 + zone*6.0) -3.0)/RADIANS ; + e_squared = 2.0 * FLATTENING - FLATTENING*FLATTENING; + e_fourth = e_squared * e_squared; + e_sixth = e_fourth * e_squared; + e_prime_sq = e_squared/(1.0 - e_squared); + sin_phi = sin(lat); + tan_phi = tan(lat); + cos_phi = cos(lat); + N = RADIUS/sqrt(1.0 - e_squared*sin_phi*sin_phi); + T = tan_phi*tan_phi; + C = e_prime_sq*cos_phi*cos_phi; + M = RADIUS*((1.0 - e_squared*0.25 -0.046875*e_fourth -0.01953125*e_sixth)*lat- + (0.375*e_squared + 0.09375*e_fourth + + 0.043945313*e_sixth)*sin(2.0*lat) + + (0.05859375*e_fourth + 0.043945313*e_sixth)*sin(4.0*lat) - + (0.011393229 * e_sixth)*sin(6.0*lat)); + A = (lon - lambda_not)*cos_phi; + A_sq = A*A; + A_fourth = A_sq*A_sq; + + /* now go ahead and compute X and Y */ + + x_utm = K_NOT*N*(A + (1.0 - T + C)*A_sq*A/6.0 + + (5.0 - 18.0*T + T*T + 72.0*C - + 58.0*e_prime_sq)*A_fourth*A/120.0); + + /* note: specific to UTM, vice general trasverse mercator. + since the origin is at the equator, M0, the M at phi_0, + always equals zero, and I won't compute it */ + + y_utm = K_NOT*(M + N*tan_phi*(A_sq/2.0 + + (5.0 - T + 9.0*C + 4.0*C*C)*A_fourth/24.0 + + (61.0 - 58.0*T + T*T + 600.0*C - + 330.0*e_prime_sq)*A_fourth*A_sq/720.0)); + + /* now correct for false easting and northing */ + + if( lat < 0) + { + y_utm +=10000000.0; + } + x_utm +=500000; + + /* adds Java function returns */ + var utmxy={}; + utmxy={x:x_utm,y:y_utm}; + return(utmxy); + } + return true; +} + + +/*-------------------------------------------------------*/ + +function utm_geo(x_utm, y_utm, zone) +{ + with(Math) + { + /* first, subtract the false easting */ + x_utm = x_utm - FALSE_EASTING; + + /* compute the necessary geodetic parameters and constants */ + + e_squared = 2.0 * FLATTENING - FLATTENING*FLATTENING; + e_fourth = e_squared * e_squared; + e_sixth = e_fourth * e_squared; + oneminuse = sqrt(1.0-e_squared); + + /* compute the footpoint latitude */ + + M = y_utm/K_NOT; + mu =M/(RADIUS*(1.0 - 0.25*e_squared - + 0.046875*e_fourth - 0.01953125*e_sixth)); + e1 = (1.0 - oneminuse)/(1.0 + oneminuse); + e1sq =e1*e1; + footpoint = mu + (1.5*e1 - 0.84375*e1sq*e1)*sin(2.0*mu) + + (1.3125*e1sq - 1.71875*e1sq*e1sq)*sin(4.0*mu) + + (1.57291666667*e1sq*e1)*sin(6.0*mu) + + (2.142578125*e1sq*e1sq)*sin(8.0*mu); + + + /* compute the other necessary terms */ + + e_prime_sq = e_squared/(1.0 - e_squared); + sin_phi = sin(footpoint); + tan_phi = tan(footpoint); + cos_phi = cos(footpoint); + N = RADIUS/sqrt(1.0 - e_squared*sin_phi*sin_phi); + T = tan_phi*tan_phi; + Tsquared = T*T; + C = e_prime_sq*cos_phi*cos_phi; + Csquared = C*C; + denom = sqrt(1.0-e_squared*sin_phi*sin_phi); + R = RADIUS*oneminuse*oneminuse/(denom*denom*denom); + D = x_utm/(N*K_NOT); + Dsquared = D*D; + Dfourth = Dsquared*Dsquared; + + lambda_not = ((-180.0 + zone*6.0) -3.0) * DEGREES_TO_RADIANS; + + + /* now, use the footpoint to compute the real latitude and longitude */ + + var lat = footpoint - (N*tan_phi/R)*(0.5*Dsquared - (5.0 + 3.0*T + 10.0*C - + 4.0*Csquared - 9.0*e_prime_sq)*Dfourth/24.0 + + (61.0 + 90.0*T + 298.0*C + 45.0*Tsquared - + 252.0*e_prime_sq - + 3.0*Csquared)*Dfourth*Dsquared/720.0); + var lon = lambda_not + (D - (1.0 + 2.0*T + C)*Dsquared*D/6.0 + + (5.0 - 2.0*C + 28.0*T - 3.0*Csquared + 8.0*e_prime_sq + + 24.0*Tsquared)*Dfourth*D/120.0)/cos_phi; + + /* adds Java function returns */ + var utmll={}; + utmll={ulat:lat,ulon:lon}; + return(utmll); + } + return true; +} + +/*------------------------------------------*/ +function getoll(Form) +{ + with(Math) + { + /* get the origin lat and lon */ + + var olatd =Form.LatDeg.value; + var olatm =Form.LatMin.value; + + var olond =Form.LonDeg.value; + var olonm =Form.LonMin.value; + + var onsdir=Form.NS.value; + var oewdir=Form.EW.value; + + /* check origin inputs */ + if( Form.DecLat.value == "" & (Form.LatDeg.value == "" & + Form.LatMin.value == "" ) ) + { + alert('Enter latitude value for the origin'); + } + else + { + if( Form.DecLat.value != "") + { + olat=1.*Form.DecLat.value; /* convert string to float*/ + } + else + { + olat=deg_min_2_deg(olatd,olatm); + } + + if (olat > 90) alert("Invalid Origin Latitude, valid range 0-90"); + if (onsdir == "S") + { + olat = -1*olat; + } + } + if ( Form.DecLon.value == "" & (Form.LonDeg.value == "" & + Form.LonMin.value == "" ) ) + { + alert('Enter longitude value for the origin'); + return false; + } + else + { + if( Form.DecLon.value != "" ) + { + olon=1.*Form.DecLon.value; + } + else + { + olon=deg_min_2_deg(olond,olonm); + } + if (olon > 180) alert("Invalid Origin Longitude, valid range 0-180"); + + if (oewdir == "W") + { + olon = -1*olon; + } + } + var oll={}; + oll={olat:olat,olon:olon}; + return (oll); + } + return true; +} + +/*-------------------------------------------------*/ +function getsll(Form) +{ + with(Math) + { + /* get the input lat and lon position to be convert */ + + var slatd =Form.SLatDeg.value; + var slatm =Form.SLatMin.value; + + var slond =Form.SLonDeg.value; + var slonm =Form.SLonMin.value; + + var snsdir=Form.SNS.value; + var sewdir=Form.SEW.value; + + /* check source inputs */ + if( Form.DecSLat.value == "" & (Form.SLatDeg.value == "" & + Form.SLatMin.value == "" ) ) + { + alert('Enter latitude value to convert'); + } + else + { + if ( Form.DecSLat.value != "" ) + { + slat=1.0*Form.DecSLat.value; + } + else + { + if (Form.SLatDeg.value == "" & Form.SLatMin.value == "" ) + { + alert('Enter latitude value to convert'); + } + else + { + slat=deg_min_2_deg(slatd,slatm); + } + } + if (slat > 90) + alert("Invalid Latitude, valid range 0-90"); + + if (snsdir == "S") + { + slat = -1*slat; + } + } + if( Form.DecSLon.value == "" & (Form.SLonDeg.value == "" & + Form.SLonMin.value == "" ) ) + { + alert('Enter longitude value to convert'); + return false; + } + else + { + if( Form.DecSLon.value != "" ) + { + slon=1*Form.DecSLon.value; + } + else + { + + slon=deg_min_2_deg(slond,slonm); + } + + if (slon > 180) alert("Invalid Longitude, valid range 0-180"); + + if (sewdir == "W") + { + slon = -1*slon; + } + } + var sll={}; + sll={slat:slat,slon:slon}; + return (sll); + } + return true; +} + +/*----------------------------------------------------------*/ + +function ll2xy(Form) +{ + with(Math) + { + /* get the source lat and lon (to be converted) */ + var sll=getsll(Form); + slat=sll.slat; + slon=sll.slon; + + /* get the origin lat and lon */ + var oll=getoll(Form); + olat=oll.olat; + olon=oll.olon; + + var origin={}; + + origin={slat:slat, slon:slon,coord_system:1, + olat:olat,olon:olon,xoffset_mtrs:0, + yoffset_mtrs:0, + rotation_angle_degs:0,rms_error:0}; + + var ll2xy= translate_coordinates(LL_TO_XY, +origin); + + /* num.toFixed() is a buildin function, + addCommas() is a custom function listed above */ + Form.Xcord.value=(ll2xy.x.toFixed(1)); + Form.Ycord.value=(ll2xy.y.toFixed(1)); + return true; + } + return true; +} + +/*----------------------------------------------*/ +function xy2ll(Form) +{ + with(Math) + { + /* get the origin lat and lon */ + var oll=getoll(Form); + olat=oll.olat; + olon=oll.olon; + + /* get the source x and y (to be converted) */ + if( Form.Xcord.value == "" | Form.Ycord.value == "") + { + alert('Enter X/Y values to convert'); + return false; + } + else + { + sx=1*Form.Xcord.value; /* convert string to float */ + sy=1*Form.Ycord.value; + } + + var origin={}; + + origin={x:sx,y:sy,coord_system:1,olat:olat, +olon:olon,xoffset_mtrs:0,yoffset_mtrs:0,rotation_angle_degs:0,rms_error:0}; + + var xy2ll= translate_coordinates(XY_TO_LL, +origin); + + + /* get the results and fill in the form */ + var slat=xy2ll.slat; /* in decimal degrees */ + var slon=xy2ll.slon; + + if (slat < 0) sns="S";else sns="N"; + if (slon < 0) sew="W";else sew="E"; + + var dmlat=deg_2_deg_min(abs(slat)); + var dmlon=deg_2_deg_min(abs(slon)); + + Form.DecSLat.value=abs(slat).toFixed(6); + Form.SLatDeg.value=dmlat.deg; + Form.SLatMin.value=dmlat.min.toFixed(4); + + Form.SNS.value=sns; + + Form.DecSLon.value=abs(slon).toFixed(6); + Form.SLonDeg.value=dmlon.deg; + Form.SLonMin.value=dmlon.min.toFixed(4); + Form.SEW.value=sew; + } + return true; +} + +/*----------------------------------------------------------- + xy2utm (call xy2ll then geo_utm for ll2utm) +-------------------------------------------------------------*/ + +function xy2utm(Form) +{ + with(Math) + { + /* get the origin lat and lon */ + var oll=getoll(Form); + olat=oll.olat; + olon=oll.olon; + + /* get the source x and y (to be converted) */ + if( Form.Xcord.value == "" | Form.Ycord.value == "") + { + alert('Enter X/Y values'); + return false; + } + else + { + sx=1*Form.Xcord.value; /* convert string to float */ + sy=1*Form.Ycord.value; + } + + var origin={}; + + origin={x:sx,y:sy,coord_system:1, + olat:olat, +olon:olon,xoffset_mtrs:0, + yoffset_mtrs:0,rotation_angle_degs:0,rms_error:0}; + + var xy2ll = translate_coordinates(XY_TO_LL, +origin); + + var slat=xy2ll.slat; /* return in degrees */ + var slon=xy2ll.slon; + + var utmzone=utm_zone(slat,slon); /* take slat and slon in degrees */ + + var slat_rad=DTOR*slat; /* decimal degrees to radius */ + var slon_rad=DTOR*slon; + + var utmxy = geo_utm(slat_rad,slon_rad,utmzone); + + /* get the results and fill in the form */ + /* check for validity of parameters */ + /* out side of grid */ + if( slat >=84.0 | slat < -80.0) + { + utmzone="outsideGrid"; + } + + /* get the results and fill in the form */ + + Form.UTMX.value=(utmxy.x.toFixed(1)); + Form.UTMY.value=(utmxy.y.toFixed(1)); + Form.UTMZone.value=utmzone; + return true; + } + return true; +} + + +/*-----------------------------------------------------------*/ + +function ll2utm(Form) +{ + with(Math) + { + /* get the source lat and lon (to be converted) */ + var sll=getsll(Form); + + slat=sll.slat; + slon=sll.slon; + + /* determine the zone for the given longitude + with 6 deg wide longitudinal strips */ + + var zone=utm_zone(slat,slon); + + var slat_rad = DTOR * slat; + var slon_rad = DTOR * slon; + + var utmxy = geo_utm(slat_rad,slon_rad,zone); + + /* out side of grid */ + if( slat >=84.0 | slat < -80.0) + { + zone="outsideGrid"; + } + + /* get the results and fill in the form */ + + Form.UTMX.value=(utmxy.x.toFixed(1)); + Form.UTMY.value=(utmxy.y.toFixed(1)); + Form.UTMZone.value=zone; + return true; + } + return true; +} + +/*---------------------------------------------------------------*/ +function utm2ll(Form) +{ + with(Math) + { + /* get the utm x and y (to be converted) */ + if( Form.UTMX.value == "" | Form.UTMY.value == "" | + Form.UTMZone.value == "") + { + alert('Enter UTM and Zone values to convert'); + return false; + } + else if ( Form.UTMZone.value < 1 | Form.UTMZone.value > 60) + { + alert('Zone is outside the valid range 1-60'); + } + else + { + xx=1.0*Form.UTMX.value; + yy=1.0*Form.UTMY.value; + zone=1.0*Form.UTMZone.value; + } + if ( Form.UNS.value == "S") + yy = yy - FALSE_NORTHING; /* lat < 0 */ + + utmll=utm_geo(xx,yy,zone); /* return lat lon in rad*/ + + /* convert rads to degs + */ + ulat = RTOD*utmll.ulat; + ulon = RTOD*utmll.ulon; + + /* fill in the form */ + + if (ulat < 0) sns="S";else sns="N"; + if (ulon < 0) sew="W";else sew="E"; + + dmlat=deg_2_deg_min(abs(ulat)); + dmlon=deg_2_deg_min(abs(ulon)); + + Form.DecSLat.value=abs(ulat).toFixed(6); + Form.SLatDeg.value=dmlat.deg; + Form.SLatMin.value=dmlat.min.toFixed(4); + + Form.SNS.value=sns; + + Form.DecSLon.value=abs(ulon).toFixed(6); + Form.SLonDeg.value=dmlon.deg; + Form.SLonMin.value=dmlon.min.toFixed(4); + + Form.SEW.value=sew; + } + return true; +} + + +/* -----------------------------------------------------------------*/ + +function utm2xy(Form) +{ + with(Math) + { + + /* convert lat and lon to xy coordinate with origin */ + /* get the origin lat and lon */ + var oll=getoll(Form); + olat=oll.olat; + olon=oll.olon; + + /* get the utm x and y (to be converted) */ + if( Form.UTMX.value == "" | Form.UTMY.value == "" | + Form.UTMZone.value == "" ) + { + alert('Enter UTM and Zone values to convert'); + return false; + } + else if ( Form.UTMZone.value < 1 | Form.UTMZone.value > 60) + { + alert('Zone is outside the valid range 1-60'); + } + else + { + xx=1.0*Form.UTMX.value; + yy=1.0*Form.UTMY.value; + zone=1.0*Form.UTMZone.value; + } + + + if ( Form.UNS.value == "S") + yy = yy - FALSE_NORTHING; /* lat < 0 */ + + /* get the UTM lat and lon (to be converted) */ + utmll=utm_geo(xx,yy,zone); /* return lat lon in rad*/ + + /* convert rads to degs + */ + ulat = RTOD*utmll.ulat; + ulon = RTOD*utmll.ulon; + + + + var origin={}; + + origin={slat:ulat,slon:ulon,coord_system:1, + olat:olat,olon:olon,xoffset_mtrs:0, + yoffset_mtrs:0, + rotation_angle_degs:0,rms_error:0}; + + var ll2xy= translate_coordinates(LL_TO_XY, +origin); + + /* num.toFixed() is a build in function, + addCommas() is a custom function listed above */ + /* Form.Xcord.value=addCommas(ll2xy.x.toFixed(3)); */ + Form.Xcord.value=(ll2xy.x.toFixed(1)); + Form.Ycord.value=(ll2xy.y.toFixed(1)); + return true; + } + return true; +} +/*-----------------------------------------------------------*/ +/* resizing_window.js copy from the following website*/ + +// Cross-browser, cross-platform support for browser +// windows that auto-resize to match a specified +// usable interior size. JavaScript code copyright 2006, +// Boutell.Com, Inc. +// +// See http://www.boutell.com/newfaq/ for more information. +// +// Permission granted to use, republish, sell and otherwise +// benefit from this code as you see fit, provided you keep +// this notice intact. You may remove comments below this line. +// +// END OF NOTICE +// +// INSTRUCTIONS: this WON'T WORK unless you do the following in the +// document that includes it. +// +// 1. Specify the right doctype at the top of your page: +// +// +// +// 2. Set the right event handlers in your body element +// (you may call other functions too, use semicolons to separate). +// Pass the interior width and height YOU want to resizingWindowLoaded. +// +// +// +// 3. BE SURE to call resizingWindowEndOfBody() before you +// close your element: +// +// +// +// +// And that's all it takes! +// +// WARNINGS: +// +// 1. In my tests, the very latest version of Opera doesn't allow +// JavaScript to resize the browser window AT ALL, even if the +// window resizing option is enabled under +// Tools->Advanced->JavaScript Options. There's not much to +// be done about that. However the code should work correctly if +// your copy of Opera does allow resizing. Note that there is +// also a small fudge factor to allow for a vertical scrollbar in +// Opera, because Opera is the only browser that can't be +// convinced to report the true interior usable space not wasted +// by a scrollbar, and we never, ever want to force a +// horizontal scrollbar unnecessarily. +// +// 2. Users with JavaScript disabled won't get the resizing behavior. +// Hey, there's no miracle cure for that! Design your page layout to +// cope adequately if the browser window is not the expected size. + +function resizingWindowIsIE() +{ + if (navigator.appName == 'Microsoft Internet Explorer') { + return true; + } + return false; +} + +function resizingWindowIsOpera() +{ + if (navigator.appName == 'Opera') { + return true; + } + return false; +} + +// We resize a maximum of three times. This allows +// the code to try to resolve any boundary conditions, +// such as scrollbars appearing or disappearing, +// in the browser's reaction to the first resize - but +// also prevents an infinite loop. + +var resizingWindowMaxResizes = 3; +var resizingWindowResizes = 0; + +var dwidth; +var dheight; + +function resizingWindowLoaded(width, height) +{ + dwidth = width; + dheight = height; + resizingWindowResizes = 0; + resizingWindowGo(); +} + +function resizingWindowEndOfBody() +{ + document.write("
\n"); +} + +function resizingWindowResized() +{ + resizingWindowGo(); +} + +function resizingWindowGo() +{ + // We're in "standards mode," so we must use + // document.documentElement, not document.body, in IE. + var width; + var height; + var x, y, w, h; + if (resizingWindowResizes == resizingWindowMaxResizes) { + return; + } + resizingWindowResizes++; + // Get browser window inner dimensions + if (resizingWindowIsIE()) { + // All modern versions of IE, including 7, give the + // usable page dimensions here. + width = parseInt(document.documentElement.clientWidth); + height = parseInt(document.documentElement.clientHeight); + } else if (resizingWindowIsOpera()) { + // This is slightly off: the width and height will include + // scrollbar space we can't really use. Compensate by + // subtracting 16 pixels of scrollbar space from the width + // (standard in Opera). Fortunately, in Firefox and Safari, + // we can use a third method that gives accurate results + // (see below). + width = parseInt(window.innerWidth) - 16; + // If there is a horizontal scrollbar this will be + // 16 pixels off in Opera. I can live with that. + // You don't design layouts on purpose with + // horizontal scrollbars, do you? (Shudder) + height = parseInt(window.innerHeight); + } else { + // Other non-IE browsers give the usable page dimensions here. + // We grab the info by discovering the visible dimensions + // of a hidden 100% x 100% div. Opera doesn't like this + // method any more than IE does. Fun! + testsize = document.getElementById('resizingWindowTestSizeDiv'); + width = testsize.scrollWidth; + height = testsize.scrollHeight; + } + // Compute the difference and add or subtract + // space as required. Notice that we don't have to + // know the dimensions of the toolbar, status bar, etc. + // All we have to do is make a relative adjustment. + if ((dwidth == width) && (dheight == height)) { + // Don't resize anymore now that it's right! + // We don't want to interfere with manual resize + resizingWindowResizes = resizingWindowMaxResizes; + return; + } + var xchange = dwidth - width; + var ychange = dheight - height; + window.resizeBy(xchange, ychange); +} + + + + diff --git a/modules/geogen.js b/modules/geogen.js new file mode 100644 index 00000000..20bc8a5d --- /dev/null +++ b/modules/geogen.js @@ -0,0 +1,812 @@ +units = { //units, their name and conversion ratio to Kilometers + "mm": { name: "Millimeters", conv: 1000000 }, + "cm": { name: "Centimeters", conv: 100000 }, + "m": { name: "Meters", conv: 1000 }, + "km": { name: "Kilometers", conv: 1 }, + "in": { name: "Inches", conv: 39370.07874 }, + "ft": { name: "Feet", conv: 3280.839895 }, + "yd": { name: "Yards", conv: 1093.613298 }, + "mi": { name: "Miles", conv: 0.621371192 }, + "nmi": { name: "Nautical Miles", conv: 0.539956803 }, + "lg": { name: "Leagues", conv: 0.207123730 }, //this is just one possible value, meaning of league differs majorly + "vr": { name: "Versta", conv: 0.937382827 }, +} +const type_names = { + "continent": ["Acai", "Adroya", "Aehesus", "Aeplaes", "Aetiveth", "Afeutoris", "Ahera", "Ahux", "Aifin", "Aipodux", "Ames", "Areth", "Aseugeon", "Atane", "Aufokica", "Auluqoth", "Aupicul", "Auquan", "Aweapari", "Baeyis", + "Beawath", "Blateron", "Bleumebela", "Blifin", "Braiyias", "Breizoth", "Brezoqall", "Bruanosia", "Buadrea", "Ceiphizuan", "Cewriograi", "Chaqon", "Chialuin", "Ciclone", "Cleikoxoa", "Cleiyezand", "Cleobuxune", "Clipuzoya", + "Clowos", "Cluamisoa", "Clulath", "Codni", "Cowane", "Cracend", "Cuphiashoth", "Cusux", "Deblas", "Dreasuin", "Dreozai", "Edax", "Eduqith", "Eheiqeon", "Ehox", "Eifugias", "Eilolish", "Eiyuwax", "Eizunai", "Ekox", "Eleuvax", + "Eobekaes", "Eodraes", "Eolovin", "Eqoqor", "Eseizela", "Esune", "Etroa", "Eubrax", "Euclax", "Eufuxura", "Eukolaes", "Euweon", "Feusuin", "Fliutazias", "Flucone", "Fuazath", "Geiwis", "Gliuboya", "Grigeucax", "Gruacitane", + "Guashushea", "Helane", "Holira", "Hoqish", "Iapath", "Iawaqul", "Iboth", "Idia", "Ipeth", "Iqall", "Irend", "Iuqenax", "Iuzoa", "Iwor", "Iworis", "Jaepixari", "Jaukroran", "Jichul", "Jubrios", "Kaphox", "Kasuin", "Kerura", + "Klikesh", "Kliocane", "Klumane", "Kraeputul", "Krahonan", "Krainura", "Kreomixias", "Krereudas", "Krifera", "Kuadox", "Kuyiocroa", "Limux", "Lopall", "Miozegrera", "Nauphuan", "Niudrica", "Nodeayuin", "Nophea", "Nuawasoa", + "Odon", "Odrus", "Ogane", "Okaecox", "Oqai", "Osesh", "Osune", "Oyune", "Peoclosend", "Phuwuan", "Pleawos", "Pleidrand", "Prerai", "Priulone", "Qeawagaes", "Qeiqastrea", "Qephiasura", "Qiagreon", "Qiopath", "Reyath", "Riaploa", + "Sainacas", "Sateron", "Serihoya", "Shonios", "Shuaditish", "Shuyun", "Slaedreth", "Slaeqabios", "Slicen", "Sliperon", "Sluraetuan", "Slutax", "Suchos", "Tegreinane", "Teophiduin", "Tibrura", "Tiotavoth", "Tizeudia", + "Trepeocai", "Treutoya", "Triqos", "Uabroris", "Uahakia", "Uawiwios", "Ubenios", "Uhios", "Upiozan", "Urai", "Uwesux", "Uwush", "Vledath", "Vleowiyush", "Vlifica", "Vragrux", "Vraikequth", "Vuyiagren", "Waiqesh", "Wetrune", + "Witrari", "Wreikucix", "Wrugith", "Xearutuin", "Xidren", "Xohix", "Xualuvax", "Yaqox", "Yegos", "Yeustror", "Yucrari", "Zaeshela", "Zaiplecaes", "Zeamuin", "Zephush"], + "isle": ["Amtara Reef", "Balcaster Island", "Belleby Enclave", "Berksea Island", "Beversby Isle", "Birmingtague Ait", "Birtown Key", "Bolnach Haven", "Brightside Ait", "Brismark Islet", "Briswater Islet", + "Brommer Chain", "Calenigan Isle", "Campbo Skerry", "Camptague Island", "Canterliers Enclave", "Cartwater Peninsula", "Casneau Holm", "Cauborough Islands", "Chiboubron Haven", "Chide Islands", "Clarenside Chain", + "Cliffsomin Ait", "Cumberchester Chain", "Derwin Isles", "Digwood Cay", "Eastgue Isles", "Eatowe Atoll", "Elcola Key", "Emgough Key", "Fairdown Enclave", "Gamsons Isle", "Ganmond Haven", "Gatileche Isle", + "Gilminster Holm", "Glenheim Archipelago", "Gracewaki Reef", "Granhead Isles", "Hadway Peninsula", "Hamliers Reef", "Haspids Enclave", "Hillsline Cay", "Hingchester Isle", "Irochill Islet", "Irridown Ait", + "Killinggamau Cay", "Killingside Cay", "Kirlet Island", "Langbiens Islet", "Lashcana Island", "Leamingshaw Cay", "Limingnear Chain", "Menbour Atoll", "Milburns Refuge", "Miniris Cay", "Morintane Isle", "Petasack Holm", + "Portgeo Archipelago", "Portterel Island", "Reidby Isles", "Reidfail Isles", "Repenmark Island", "Robmont Island", "Saglodge Holm", "Salisgeo Reef", "Sedgeree Skerry", "Shawris Archipelago", "Stafcouche Cay", + "Staflams Skerry", "Stokemiota Refuge", "Susduff Ait", "Susleche Refuge", "Taunnet Islet", "The Arching Isle", "The Arid Islands", "The Burning Isles", "The Castaway Islet", "The Colossal Key", "The Deep Skerry", + "The Defeated Haven", "The Diamond Isle", "The Distant Ait", "The Dread Atoll", "The Ethereal Enclave", "The Faraway Enclave", "The Fiery Holm", "The Fiery Island", "The Flowing Isles", "The Frozen Peninsula", + "The Glowing Islet", "The Grim Reef", "The Heartless Haven", "The Hollow Islet", "The Jellyfish Holm", "The Laughing Ait", "The Light Chain", "The Lost Enclave", "The Mysterious Archipelago", "The Mysterious Key", + "The Pain Key", "The Peaceful Haven", "The Pearl Chain", "The Penguin Refuge", "The Raging Archipelago", "The Relentless Chain", "The Sad Peninsula", "The Sanctum Chain", "The Shadowed Ait", "The Shaking Isles", + "The Shark Enclave", "The Shrine Cay", "The Silver Peninsula", "The Skeleton Ait", "The Skeleton Cay", "The Skeleton Refuge", "The Starfish Haven", "The Sunny Isles", "The Torpedo Island", "The Virgin Skerry", + "The Wasting Ait", "The Waterless Skerry", "The Waveless Isle", "The Windy Island", "The Yelling Ait", "Tisliers Island", "Turgonie Chain", "Ventmiota Isles", "Virworth Peninsula", "Wallsevain Refuge", "Wilwin Reef", + "Windminster Islet", "Wynrial Chain"], + "island": ["Lighthill", "Strongmill", "Whitebridge", "Woodwall", "Deepsnow", "Irondell", "Eriwynne", "Fairwinter", "Deepwater", "Waterton", "Fallmage", "Rosedell", "Westerdragon", "Oldfield", "Morton", "Ironshade", "Merridale", + "Westcliff", "Flowercliff", "Vertkeep", "Clearmont", "Witchlyn", "Deerdell", "Hollowcastle", "Janwynne", "Clearden", "Oldburn", "Flowermeadow", "Linmoor", "Westerbeach", "Deepwolf", "Oakgate", "Southgrass", "Moormeadow", + "Qauar", "Jipon", "Venela", "Martique", "Ugada", "Maurania", "Turnistan", "Russip", "Conada", "Svalbard", "Jan Mayen", "Mari Island", "Angua", "Baruda", "Monolia", "Ameri", "Samoa"], + "freshwater": ["Shaded Lake", "Vast Expanse", "Neglected Gorge", "Glistening Loch", "Blythedows Lake", "Chamterre Lake", "Winterlan Gorge", "Wadelem Loch", "Ridgelis Expanse", "Bradbron Pond", "Calm Lake", "Mirrored Waters", + "Unstable Basin", "Barren Reservoir", "Buchstino Lagoon", "Ferquet Expanse", "Cardpids Reservoir", "Gatilin Cove", "Bloomslet Expanse", "Antitos Cove", "Western Waters", "Pleasant Depths", "Vast Reservoir", + "Peaceful Reservoir", "Harvern Basin", "Limingmeda Depths", "Wellingronto Lagoon", "Causahead Gorge", "Warming Reservoir", "Limingside Reservoir", "New Basin", "Cursed Basin", "Crystal Waters", "Ugly Lagoon", + "Croyville Pond", "Landare Lake", "Franram Loch", "Hillsval Domain", "Smithrial Basin", "Suntrie Gorge", "Serene Lagoon", "Cobalt Loch", "Arrowhead Lake", "Iris Lagoon", "Manirood Shallows", "Appleware Shallows", + "Buckinglam Basin", "Amespids Waters", "Gaulden Pond", "Margar Lake", "Coral Depths", "Boiling Gorge", "Peaceful Waters", "Flowing Shallows", "Walhurst Domain", "Wolffail Basin", "Stratsevain Lagoon", "Davellin Lake", + "Morinneau Cove", "Huntbalt Expanse"], + "salt": ["Gray Domain", "Wasor Basin", "Orocastle Lake", "Cowantrie Shallows", "Belldare Waters", "Bruderley Gorge", "Eckpond Cove", "Eastern Cove", "Coral Pond", "Shaded Waters", "Crocodile Depths", "Cottlecam Lake", + "Midalants Lake", "Onofolk Reservoir", "Herewin Depths", "Grimrial Waters", "Sherden Lagoon", "Wasteful Shallows", "Cursed Reservoir", "Uncanny Pond", "Wrinkled Cove", "Rossoll Lagoon", "Huntingpond Basin", + "Grandburn Waters", "Barnrey Domain", "Stokeleche Pond", "Kapusgus Cove", "Furthest Gorge", "Hungry Expanse", "Cursed Domain", "Narrow Depths", "Corngan Domain", "Estou Pond", "Bridgediac Loch", "Liverbiens Gorge", + "Burstry Waters", "Caubiens Waters"] +} + + + + + +const natural_types = +{ + land: { + area: [ + "scrub", "heath", "moor", "wetland", "grassland", "fell", "bare_rock", "scree", "shingle", "sand", "mud", "cave_entrance", "sinkhole", "rock", "hill", "valley", "peninsula" + ], features: [ + "wood", "tree_row", "tree" + ], points: [ + "volcano", "cape", "peak", "spring", "hot_spring", "geyser", "stone ", "saddle", "tree", "cave_entrance", "sinkhole", "rock" + ], lines: [ + "river_terrace", "ridge", "arete", "cliff" + ] + }, water: { + area: [ + "water", "glacier", "bay", "strait", "beach", "reef" + ], lines: [ + "coastline" + ] + } +} + + +burgs_groups = [ + { type: "city", pop: 100000 }, + { type: "town", pop: 2000 }, + { type: "village", pop: 100 }, + { type: "hamlet", pop: 10 }, + { type: "isolated_dwelling", pop: -1 } +] + +numRegExp = new RegExp("[0-9]+"); + +const ln = "\r\n"; + +const cen_lat = 51.50265085; +const cen_lon = -3.16268235; + +const rotation_angle_degs = 1; + + +// using the data on +// https://en.wikipedia.org/wiki/List_of_United_States_cities_by_area +// the average of the square meter per person in city cant to 46.4906806 +avg_sqm_pp = 46.5; + +function gen_geodata(type) { + + + let data = ""; + let ext = ""; + let dset = {}; + + //work out the scale in the units chosen by using the conversion ratio times by 1000 for meters + const scale = (distanceScale.value * unit_to_km(distanceUnit.value)) * 1000; + console.log(scale + " meters per pixel"); + + dset.settlements = dataget_settlements(); + dset.points = dataget_points(); + dset.states = dataget_states(); + dset.coastlines = dataget_bodies("#coastline > *"); + dset.lakes = dataget_bodies("#freshwater > *, #salt > *"); + + if (type === "osm") { + data = gen_osm(dset, scale); + ext = ".osm"; + } else if (type === "json") { + data = gen_json(dset, scale); + ext = ".geo.json"; + } + return { data: data, ext: ext } +} + +function dataget_settlements() { + let settlements = []; + for (let i = 0, c = 0; i < pack.burgs.length; i++) { + let burg = pack.burgs[i]; + let type = ""; + if ("i" in burg) { + settlements[c] = burg; + //replace population with actual value, scale * 1000 + pop = settlements[c].population * populationRate.value * 100; + settlements[c].population = pop; + for (let t = 0; t < burgs_groups.length; t++) { + if (pop > burgs_groups[t].pop) { + type = burgs_groups[t].type; + break; + } + } + settlements[c].type = type; + burgs_groups + c++; + } + } + return settlements; +} + +function dataget_points() { + let points = []; + for (let i = 0, c = 0; i < grid.points.length; i++) { + let point = grid.points[i]; + if (point.length > 0) { + points[c] = point; + c++; + } + } + return points; +} + +function dataget_states() { + statesCollectStatistics(); + const node_states = document.getElementById("statesHalo").childNodes; + let states = []; + for (var i = 0; i < node_states.length; i++) { + states[i] = pack.states[i]; + if (states[i].center) { + states[i].x = pack.cells.p[pack.states[i].center][0]; + states[i].y = pack.cells.p[pack.states[i].center][1]; + } + states[i].path = node_states[i]; + } + return states; + + function statesCollectStatistics() { + const cells = pack.cells, states = pack.states; + states.forEach(s => s.cells = s.area = s.burgs = s.rural = s.urban = 0); + + for (const i of cells.i) { + if (cells.h[i] < 20) continue; + const s = cells.state[i]; + states[s].cells += 1; + states[s].area += cells.area[i]; + states[s].rural += cells.pop[i]; + if (cells.burg[i]) { + states[s].urban += pack.burgs[cells.burg[i]].population; + states[s].burgs++; + } + } + } +} + +function dataget_bodies(query) { + let nodes = document.querySelectorAll(query); + let bodies = []; + for (var i = 0; i < nodes.length; i++) { + let match = nodes[i].id.match(numRegExp); + let f_id = Number(match[0]); + if (f_id in pack.features) { + bodies[i] = pack.features[f_id]; + if (type_names[bodies[i].group] === undefined) { + console.log(bodies[i]); + } + bodies["name"] = type_names[bodies[i].group][i]; + bodies[i].path = nodes[i]; + } else { + console.log("no feature by with id: " + f_id); + } + } + return bodies; +} + +function gen_osm(data, scale) { + let cur_id = 1; + + let mpoints = osm_settlements_to_features(data.settlements, cur_id, scale); + cur_id = mpoints.last_id; + let spoints = osm_states_to_features(data.states, cur_id, scale); + cur_id = spoints.last_id; + let cpoints = osm_coastlines_to_features(data.coastlines, cur_id, scale); + cur_id = cpoints.last_id; + let lpoints = osm_lakes_to_features(data.lakes, cur_id, scale); + cur_id = lpoints.last_id; + + let osm = "" + ln; + osm += "" + ln; + osm += mpoints.nodes; + osm += spoints.nodes; + osm += cpoints.nodes; + osm += lpoints.nodes; + osm += mpoints.ways + osm += spoints.ways; + osm += cpoints.ways; + osm += lpoints.ways; + osm += mpoints.relations + osm += spoints.relations; + osm += cpoints.relations; + osm += lpoints.relations; + osm += ""; + + return osm; +} + +function gen_json(data, scale) { + let cur_id = 1; + let features = []; + //fpoints = json_points_to_features(data.points, cur_id, scale); + //cur_id = fpoints.last_id; + let mpoints = json_settlements_to_features(data.settlements, cur_id, scale); + cur_id = mpoints.last_id; + let spoints = json_states_to_features(data.states, cur_id, scale); + cur_id = spoints.last_id; + let cpoints = json_coastlines_to_features(data.coastlines, cur_id, scale); + cur_id = cpoints.last_id; + let lpoints = json_lakes_to_features(data.lakes, cur_id, scale); + cur_id = lpoints.last_id; + + //features = features.concat(fpoints.json); + features = features.concat(mpoints.json); + features = features.concat(spoints.json); + features = features.concat(cpoints.json); + features = features.concat(lpoints.json); + + const json_data = { "type": "FeatureCollection", "features": features }; + + return JSON.stringify(json_data, null, 2); +} + + +//--------------osm functions------------------- + +function osm_settlements_to_features(data, id, scale) { + let nodes = ""; + let ways = ""; + let relations = ""; + let tagcats = {}; + for (let i = 0; i < data.length; i++) { + let members = []; + id++; + boundry = xy_to_boundry(data[i].x, data[i].y, data[i].population, scale); + start_id = id; + + nodes += latlongs_to_nodes(boundry, id); + id += boundry.length + 1; + tagcats = { + type: "boundry", boundary: "administrative", admin_level: "6", + name: data[i].name + }; + tags = gen_tags(tagcats); + ways += gen_way(id, start_id, boundry.length, tags); + members.push({ id: id, type: "way", role: "outer" }); + id += boundry.length + 1; + start_id = id; + tagcats = { + type: "place", place: data[i].type, name: data[i].name, + cell: data[i].cell, region: data[i].region, area: (Math.PI * data.population ^ 2), culture: data[i].culture, + population: data[i].population, feature: data[i].feature, + capital: data[i].capital, port: data[i].port, settlement_id: data[i].i, x: data[i].x, y: data[i].y + }; + tags = gen_tags(tagcats); + nodes += latlong_to_node(xy_to_coords(data[i].x * scale, data[i].y * scale), id, tags); + members.push({ id: id, type: "node", role: "admin_centre" }); + id++; + + tagcats = { + type: "boundry", boundary: "administrative", border_type: "city", admin_level: "6", designation: "principal_area", + name: data[i].name, long_name: data[i].long_name, "is_in:country": data[i].country, "is_in:country_code": data[i].country_code, + cell: data[i].cell, region: data[i].region, area: (Math.PI * data.population ^ 2), culture: data[i].culture, + population: data[i].population, feature: data[i].feature, + capital: data[i].capital, port: data[i].port, settlement_id: data[i].i, x: data[i].x, y: data[i].y + }; + tags = gen_tags(tagcats); + relations += gen_relation(id++, tags, members); + } + return { nodes: nodes, ways: ways, relations: relations, last_id: id }; +} + +function osm_states_to_features(data, id, scale) { + let nodes = ""; + let ways = ""; + let relations = ""; + let tagcats = {}; + let tags = ""; + for (let i = 0; i < data.length; i++) { + let boundries = flatten_multi_svg(data[i].path, 1000); + + let members = []; + for (let b = 0; b < boundries.length; b++) { + if (boundries[b].length > 0) { + id++; + + start_id = id; + tagcats = { + type: "place", place: "region", + region_id: b + }; + let region_tags = gen_tags(tagcats); + nodes += latlongs_to_nodes(xypath_to_latlong(boundries[b], scale), id); + id += boundries[b].length + 1; + members.push({ id: id, type: "way", role: "outer" }); + ways += gen_way(id, start_id, boundries[b].length, region_tags); + } + } + if (data[i].center) { + id++; + tagcats = { + type: "place", place: "country", name: data[i].name, + cell: data[i].cell, region: data[i].region, area: (Math.PI * data.population ^ 2), culture: data[i].culture, + x: data[i].x, y: data[i].y, center: data[i].center, state_id: data[i].i + }; + + tags = gen_tags(tagcats); + nodes += latlong_to_node(xy_to_coords(data[i].x * scale, data[i].y * scale), id, tags); + members.push({ id: id, type: "node", role: "admin_centre" }); + id++; + } + tagcats = { + type: "multipolygon", boundary: "administrative", border_type: "city", admin_level: "6", designation: "principal_area", + name: data[i].name, long_name: data[i].long_name, "country": data[i].name, "country_code": data[i].i, + + state_id: data[i].i, color: data[i].color, expansionism: data[i].expansionism, capital: data[i].capital, + state_type: data[i].type, center: data[i].center, culture: data[i].culture, cells: data[i].cells, area: data[i].area, + rural: data[i].rural, urban: data[i].urban, cities: data[i].burgs, x: data[i].x, y: data[i].y + }; + tags = gen_tags(tagcats); + relations += gen_relation(id++, tags, members) + + } + return { nodes: nodes, ways: ways, relations: relations, last_id: id }; +} + +function osm_coastlines_to_features(data, id, scale) { + let nodes = ""; + let ways = ""; + let relations = ""; + let tagcats = {}; + for (let i = 0; i < data.length; i++) { + let members = []; + id++; + boundry = xypath_to_latlong(flatten_svg(data[i].path, 1000), scale); + start_id = id; + nodes += latlongs_to_nodes(boundry, id); + id += boundry.length + 1; + tagcats = { + type: "natural", natural: "coastline", feature_id: data[i].i + }; + tags = gen_tags(tagcats); + ways += gen_way(id, start_id, boundry.length, tags); + members.push({ id: id, type: "way", role: "outer" }); + id++ + let type = natural_types.land.area[Math.floor((Math.random() * natural_types.land.area.length))]; + tagcats = { + type: "natural", natural: type, feature_id: data[i].i + }; + tags = gen_tags(tagcats); + + ways += gen_way(id, start_id, boundry.length, tags); + members.push({ id: id, type: "way", role: "outer" }); + + let name = type_names[data[i].group][i]; + tagcats = { + type: "place", place: data[i].group, + feature_id: data[i].i, name: name, body: data[i].body, border: data[i].border, + cells: data[i].cells, land: data[i].land, mtype: data[i].type + }; + tags = gen_tags(tagcats); + + relations += gen_relation(id++, tags, members); + + + } + return { nodes: nodes, ways: ways, relations: relations, last_id: id }; +} + +function osm_lakes_to_features(data, id, scale) { + let nodes = ""; + let ways = ""; + let relations = ""; + let tagcats = {}; + for (let i = 0; i < data.length; i++) { + id++; + boundry = xypath_to_latlong(flatten_svg(data[i].path, 1000), scale); + start_id = id; + nodes += latlongs_to_nodes(boundry, id); + id += boundry.length + 1; + let name = type_names[data[i].group][i]; + tagcats = { + type: "natural", natural: "water", water: "lake", + name: name, body: data[i].body, border: data[i].border, + cells: data[i].cells, land: data[i].land, mtype: data[i].mtype, + feature_id: data[i].i + }; + if (data.group === "salt") { + tagcats.salt = "yes"; + } + tags = gen_tags(tagcats); + ways += gen_way(id, start_id, boundry.length, tags); + id++; + } + return { nodes: nodes, ways: ways, relations: relations, last_id: id }; +} + +//--------------osm functions------------------- + +function gen_way(way_id, start_id, num, tags) { + way = "\t" + ln; + + way += gen_nd(start_id, num); + way += tags; + way += "\t" + ln; + return way; +} + +function gen_relation(id, tags, members, tagcats) { + relation = ""; + relation += "\t" + ln; + relation += gen_members(members); + relation += tags; + relation += "\t" + ln; + return relation; +} + +function gen_members(ids) { + members = ""; + for (i = 0; i < ids.length; ++i) { + members += "\t\t" + ln; + } + return members; +} + +function gen_nd(id, num) { + last_nd = "\t\t" + ln; + nd = ""; + for (i = 0; i < num; ++i) { + nd += "\t\t" + ln; + } + nd += last_nd; + return nd; +} + +function latlongs_to_nodes(latlongs, id) { + nodes = ""; + for (k in latlongs) { + ll = latlongs[k]; + nodes += "\t" + ln; + + } + return nodes; +} + +function latlong_to_node(ll, id, tags) { + node = ""; + node += "\t" + ln; + node += tags; + node += "\t" + ln; + return node; +} + + +function gen_tags(tagcats) { + let tags = ""; + const blk = "Unimplemented"; + for (k in tagcats) { + tags += "\t\t" + ln; + } + //tags += "\t\t" + ln; + //tags += "\t\t" + ln; + return tags; +} + +//--------------json functions------------------- + +function json_settlements_to_features(data, id, scale) { + allPoints = []; + for (let i = 0; i < data.length; i++) { + id++; + + pop = data[i].population; + let bound = xy_to_boundry(data[i].x, data[i].y, pop, scale); + let polygon = latlongs_to_polygon(bound); + allPoints[i] = { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [polygon] + }, + "properties": { + "name": data[i].name, "settlement_id": data[i].i, "feature": data[i].feature, "capital": data[i].capital, "port": data[i].port, "AREA_CODE": data[i].cell, "POLYGON_ID": id, "UNIT_ID": data[i].region, "HECTARES": (Math.PI * pop ^ 2), "DESCRIPT0": "CIVIL ADMINISTRATION AREA", "CULTURE": data[i].culture, "POPULATION": pop + } + } + } + return { json: allPoints, last_id: id }; + +} + +function json_states_to_features(data, id, scale) { + allPoints = []; + for (let i = 0; i < data.length; i++) { + id++; + + pop = data[i].population; + let boundries = flatten_multi_svg(data[i].path, 1000); + let multiPolygon = [] + for (let b = 1; b < boundries.length; b++) { + multiPolygon[b] = latlongs_to_polygon(xypath_to_latlong(boundries[b], scale)); + } + allPoints[i] = { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [multiPolygon] + }, + "properties": { + "name": data[i].name, "state_id": data[i].i, "feature": data[i].feature, "capital": data[i].capital, "port": data[i].port, "AREA_CODE": data[i].cell, "POLYGON_ID": id, "UNIT_ID": data[i].region, "HECTARES": (Math.PI * pop ^ 2), "DESCRIPT0": "CIVIL ADMINISTRATION AREA", "CULTURE": data[i].culture, "POPULATION": pop + } + } + } + return { json: allPoints, last_id: id }; + +} + +function json_coastlines_to_features(data, id, scale) { + allPoints = []; + for (let i = 0; i < data.length; i++) { + id++; + + pop = data[i].population; + let bound = flatten_svg(data[i].path, 1000); + let polygon = latlongs_to_polygon(xypath_to_latlong(bound, scale)); + allPoints[i] = { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [polygon] + }, + "properties": { + "name": data[i].name, "feature_id": data[i].i, "feature": data[i].feature, "capital": data[i].capital, "port": data[i].port, "AREA_CODE": data[i].cell, "POLYGON_ID": id, "UNIT_ID": data[i].region, "HECTARES": (Math.PI * pop ^ 2), "DESCRIPT0": "CIVIL ADMINISTRATION AREA", "CULTURE": data[i].culture, "POPULATION": pop + } + } + } + return { json: allPoints, last_id: id }; + +} + +function json_points_to_features(data, id, scale) { + allPoints = []; + for (let i = 0; i < data.length; i++) { + id++; + [x, y] = data[i]; + ll = xy_to_coords(x * scale, y * scale); + allPoints[c] = { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ll.slon, ll.slat] //geojson is [lon,lat] + }, + "properties": { + "name": "Point: " + (c + 1), + "POINT_ID": id + } + } + } + return { json: allPoints, last_id: id }; +} + +//--------------json functions------------------- + +function gen_settlements_props(data, it) { + let props = { + "name": data[i].name, + "settlement_id": data[i].i, + "feature": data[i].feature, + "capital": data[i].capital, + "port": data[i].port, + "AREA_CODE": data[i].cell, + "POLYGON_ID": it, + "UNIT_ID": data[i].region, + "HECTARES": (Math.PI * data.population ^ 2), + "DESCRIPT0": "CIVIL ADMINISTRATION AREA", + "CULTURE": data[i].culture, + "POPULATION": data.population + } + return props; +} + +function gen_state_props(data, it) { + let props = { + "name": data.name, + "state_id": data.i, + "color": data.color, + "capital": data.capital, + "expansionism": data.expansionism, + "type": data.type, + "expansionism": data.expansionism, + "center": data.center, + "culture": data.culture, + "cells": data.cells, + "area": data.area, + "rural": data.rural, + "urban": data.urban, + "cities": data.burgs, + "AREA_CODE": data.cell, + "POLYGON_ID": it, + "HECTARES": (Math.PI * data.population ^ 2), + "DESCRIPT0": "CIVIL ADMINISTRATION AREA", + "POPULATION": data.population + } + return props; +} + +function gen_coastline_props(data) { + let props = { + "name": data.name, + "state_id": data.i, + } + return props; +} + +//--------------generic functions------------------- + +function flatten_multi_svg(path, num) { + const len = path.getTotalLength(); + const elem = path.pathSegList.numberOfItems; + var paths = []; + var nums = 0; + var poly = 0; + paths[poly] = []; + for (var i = 0; i < elem; i++) { + item = path.pathSegList.getItem(i); + if (item.pathSegType === 2) { + if (poly > 0 && i > 0) { + let fp = paths[poly][0]; + paths[poly][nums] = { x: fp.x, y: fp.y }; + } + poly++; + paths[poly] = []; + nums = 0; + } + paths[poly][nums] = { x: item.x, y: item.y }; + nums++; + } + return paths; +} + +function flatten_multi_svg2(path, num) { + var len = path.getTotalLength(); + var d = []; + var c = 0; + var poly = 0; + poly++; + c = 0; + d[poly] = []; + p = path.getPointAtLength(0); + d[poly][c] = { x: p.x, y: p.y }; + incr = (len / num); + + for (var i = incr; i <= len; i += incr) { + item = path.getPathSegAtLength(i); + switch (item.pathSegType) { + case 2: + poly++; + c = 0; + d[poly] = []; + d[poly][c] = { x: item.x, y: item.y }; + case 4: + var skip = ""; + } + p = path.getPointAtLength(i); + d[poly][c] = { x: p.x, y: p.y }; + c++; + } + return d; +} + +function flatten_svg(path, num) { + var len = path.getTotalLength(); + var d = []; + var c = 0; + c = 0; + d = []; + p = path.getPointAtLength(0); + d[c] = { x: p.x, y: p.y }; + //d[poly] = []; + //var p = path.getPointAtLength(0); + //d[poly][c] = {x:p.x, y:p.y}; + incr = (len / num); + + for (var i = incr; i <= len; i += incr) { + p = path.getPointAtLength(i); + d[c] = { x: p.x, y: p.y }; + c++; + } + d[c] = { x: d[0].x, y: d[0].y }; + return d; +} + + + +function unit_to_km(unit) { + conv = 1; + name = "Unknown"; + if (unit in units) { //ignoring custom units atm + name = units[unit].name; + conv = units[unit].conv; + } + return conv; +} + +function xy_to_coords(x, y) { + porg = {}; + porg.x = x; + porg.y = -y; + porg.rotation_angle_degs = rotation_angle_degs; + porg.xoffset_mtrs = 0; + porg.yoffset_mtrs = 0; + porg.olon = 0.1; + porg.olat = 0.1; + return translate_coordinates(1, porg); +} + +function dict_to_array(dict) { + return Object.keys(dict).map(function (key) { + return dict[key]; + }) +} + +function xy_to_boundry(x, y, pop, scale) { + let n = 20; + + // The radius of circle, which area is the places's area + // which is worked out by place's population * average m^2 per person + let r = Math.sqrt((avg_sqm_pp * pop) / Math.PI); + + if (r < 20) { + r = 20; + } + let boundry = []; + x *= scale; + y *= scale; + for (let i = 0; i < n; i++) { + x_new = x + Math.cos((2 * Math.PI / n) * i) * r; + y_new = y + Math.sin((2 * Math.PI / n) * i) * r; + boundry[i] = xy_to_coords(x_new, y_new); + } + boundry[boundry.length] = boundry[0]; + return boundry; +} + +function xypath_to_latlong(xys, scale) { + latlongs = [] + for (let i = 0; i < xys.length; i++) { + let xy = xys[i]; + x = xy.x * scale; + y = xy.y * scale; + latlongs[i] = xy_to_coords(x, y); + } + return latlongs; + +} + +function latlongs_to_polygon(latlongs) { + polygon = []; + for (k in latlongs) { + ll = latlongs[k]; + polygon[k] = [ll.slon, ll.slat]; //geojson is [lon,lat] + } + return polygon; +} \ No newline at end of file diff --git a/modules/save-and-load.js b/modules/save-and-load.js index 6a3a1233..8998e77b 100644 --- a/modules/save-and-load.js +++ b/modules/save-and-load.js @@ -183,6 +183,48 @@ function saveMap() { console.timeEnd("saveMap"); } +// Save in a geo-format, an lan long global coordinate format +function saveGeo(type) { + if (customization) {tip("Map cannot be saved when is in edit mode, please exit the mode and re-try", false, "error"); return;} + console.time("saveOSM"); + const date = new Date(); + const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); + const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator"; + const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|"); + const options = [distanceUnit.value, distanceScale.value, areaUnit.value, heightUnit.value, heightExponent.value, temperatureScale.value, + barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate.value, urbanization.value, + equatorOutput.value, equidistanceOutput.value, temperatureEquatorOutput.value, temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds, null, 2)].join("|"); + const coords = JSON.stringify(mapCoordinates, null, 2); + const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); + const notesData = JSON.stringify(notes, null, 2); + + // set transform values to default + svg.attr("width", graphWidth).attr("height", graphHeight); + const transform = d3.zoomTransform(svg.node()); + viewbox.attr("transform", null); + const svg_xml = (new XMLSerializer()).serializeToString(svg.node()); + + let geodata = gen_geodata(type); + + let data = geodata.data; + let ext = geodata.ext; + + const dataBlob = new Blob([data], {type: "text/plain"}); + const dataURL = window.URL.createObjectURL(dataBlob); + const link = document.createElement("a"); + link.download = "fantasy_map_" + Date.now() + ext; + link.href = dataURL; + document.body.appendChild(link); + link.click(); + + // restore initial values + svg.attr("width", svgWidth).attr("height", svgHeight); + zoom.transform(svg, transform); + + window.setTimeout(function() {window.URL.revokeObjectURL(dataURL);}, 2000); + console.timeEnd("saveOSM"); +} + function uploadFile(file, callback) { console.time("loadMap"); const fileReader = new FileReader(); diff --git a/modules/ui/general.js b/modules/ui/general.js index 6597ac15..b588f034 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -211,6 +211,8 @@ document.addEventListener("keydown", function(event) { if (key === 118) regenerateMap(); // "F7" for new map else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs else if (key === 9) {toggleOptions(event); event.preventDefault();} // Tab to toggle options + else if (shift && key === 79) saveGeo("osm"); // Shift + "O" to save as OSM + else if (shift && key === 74) saveGeo("json"); // Shift + "J" to save as JSON else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file diff --git a/modules/ui/options.js b/modules/ui/options.js index a9455569..55a44f09 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -903,6 +903,8 @@ document.getElementById("sticked").addEventListener("click", function(event) { else if (id === "loadMap") mapToLoad.click(); else if (id === "zoomReset") resetZoom(1000); else if (id === "saveMap") saveMap(); + else if (id === "saveOSM") saveGeo("osm"); + else if (id === "saveJSON") saveGeo("json"); else if (id === "saveSVG") saveAsImage("svg"); else if (id === "savePNG") saveAsImage("png"); if (id === "saveMap" || id === "saveSVG" || id === "savePNG") toggleSavePane();