################################################# # routines to set up, transform and manage local weather # Thorsten Renk, April 2010 ################################################# var calc_geo = func(clat) { lon_to_m = math.cos(clat*math.pi/180.0) * lat_to_m; m_to_lon = 1.0/lon_to_m; } ################################### # effect volume management loop ################################### var effect_volume_loop = func { var evNode = props.globals.getNode("local-weather/effect-volumes", 1).getChildren("effect-volume"); var viewpos = geo.viewer_position(); foreach (var e; evNode) { var flag = 0; #default assumption is that we're not in the volume var geometry = e.getNode("geometry").getValue(); var elat = e.getNode("position/latitude-deg").getValue(); var elon = e.getNode("position/longitude-deg").getValue(); var ealt_min = e.getNode("position/min-altitude-ft").getValue() * ft_to_m; var ealt_max = e.getNode("position/max-altitude-ft").getValue() * ft_to_m; var rx = e.getNode("volume/size-x").getValue(); var ry = e.getNode("volume/size-y").getValue(); var phi = e.getNode("volume/orientation-deg").getValue(); phi = phi * math.pi/180.0; var evpos = geo.Coord.new(); evpos.set_latlon(elat,elon,ealt_min); var d = viewpos.distance_to(evpos); #print(d); var active_flag = e.getNode("active-flag").getValue(); # see if the node was active previously if ((viewpos.alt() > ealt_min) and (viewpos.alt() < ealt_max)) # we are in the correct alt range { if (geometry == 1) # we have a cylinder { if (d < rx) {flag =1;} } if (geometry == 2) # we have an elliptic shape { # first get unrotated coordinates var xx = (viewpos.lon() - evpos.lon()) * lon_to_m; var yy = (viewpos.lat() - evpos.lat()) * lat_to_m; # then rotate to align with the shape var x = xx * math.cos(phi) - yy * math.sin(phi); var y = yy * math.cos(phi) + xx * math.sin(phi); # then check elliptic condition if ((x*x)/(rx*rx) + (y*y)/(ry*ry) <1) {flag = 1;} } if (geometry == 3) # we have a rectangular shape { # first get unrotated coordinates var xx = (viewpos.lon() - evpos.lon()) * lon_to_m; var yy = (viewpos.lat() - evpos.lat()) * lat_to_m; # then rotate to align with the shape var x = xx * math.cos(phi) - yy * math.sin(phi); var y = yy * math.cos(phi) + xx * math.sin(phi); # then check rectangle condition if ((x>-rx) and (x-ry) and (y probably obsolete as CVS patch reads out /local-weather/current setprop("environment/thermal-lift",L); } ########################################################### # randomize positions of clouds created in a fixed pattern ########################################################### var randomize_pos = func (type, alt_var, pos_var_x, pos_var_y, dir) { # it is rather stupid coding to force the randomization call after the streak call, and I'll probably # restructure the whole thing soon var cloudNode = props.globals.getNode("local-weather/clouds", 1).getChildren("cloud"); foreach (var e; cloudNode) { var call = e.getNode("type",1).getValue(); if (call=="tmp_cloud") { var e_lat = e.getNode("position/latitude-deg"); var e_long = e.getNode("position/longitude-deg"); var e_alt = e.getNode("position/altitude-ft"); var slat = e_lat.getValue(); var slon = e_long.getValue(); var salt = e_alt.getValue(); var x = pos_var_x * 2.0 * (rand() -0.5); var y = pos_var_y * 2.0 * (rand() -0.5); slat = slat + m_to_lat * (y * math.cos(dir) - x * math.sin(dir)); slon = slon + m_to_lon * (x * math.cos(dir) + y * math.sin(dir)); salt = (salt + alt_var * 2 * (rand() - 0.5)); e_lat.setValue(slat); e_long.setValue(slon); e_alt.setValue(salt); e.getNode("type").setValue(type); if (type=="Cumulonimbus (rain)") # we add a rain model { var path = "Models/Weather/rain2.xml"; create_cloud("Rain", path, slat, slon, salt, 0.0, 0); create_effect_volume(1, slat, slon, 2000.0, 2000.0, 0.0, 0.0, salt, 8000, 0.3, -1, -1, -1,0); } } # end if } # end foreach } ########################################################### # select a cloud model ########################################################### var select_cloud_model = func(type, subtype) { var rn = rand(); var path="Models/Weather/blank.ac"; if (type == "Cumulus"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/cumulus_small_shader1.xml";} else if (rn > 0.6) {path = "Models/Weather/cumulus_small_shader2.xml";} else if (rn > 0.4) {path = "Models/Weather/cumulus_small_shader3.xml";} else if (rn > 0.2) {path = "Models/Weather/cumulus_small_shader4.xml";} else {path = "Models/Weather/cumulus_small_shader5.xml";} } else if (subtype == "large") { if (rn > 0.83) {path = "Models/Weather/cumulus_shader1.xml";} else if (rn > 0.664) {path = "Models/Weather/cumulus_shader2.xml";} else if (rn > 0.498) {path = "Models/Weather/cumulus_shader3.xml";} else if (rn > 0.332) {path = "Models/Weather/cumulus_shader4.xml";} else if (rn > 0.166) {path = "Models/Weather/cumulus_shader5.xml";} else {path = "Models/Weather/cumulus_shader6.xml";} } } else if (type == "Altocumulus"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/altocumulus_shader6.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_shader7.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_shader8.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_shader9.xml";} else {path = "Models/Weather/altocumulus_shader10.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/altocumulus_shader1.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_shader2.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_shader3.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_shader4.xml";} else {path = "Models/Weather/altocumulus_shader5.xml";} } } else if (type == "Stratus (structured)"){ if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/altocumulus_layer6.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_layer7.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_layer8.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_layer9.xml";} else {path = "Models/Weather/altocumulus_layer10.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/altocumulus_layer1.xml";} else if (rn > 0.6) {path = "Models/Weather/altocumulus_layer2.xml";} else if (rn > 0.4) {path = "Models/Weather/altocumulus_layer3.xml";} else if (rn > 0.2) {path = "Models/Weather/altocumulus_layer4.xml";} else {path = "Models/Weather/altocumulus_layer5.xml";} } } else if ((type == "Cumulonimbus") or (type == "Cumulonimbus (rain)")) { if (subtype == "small") { if (rn > 0.5) {path = "Models/Weather/cumulonimbus_small1.xml";} else {path = "Models/Weather/cumulonimbus_small2.xml";} } else if (subtype == "large") { if (rn > 0.5) {path = "Models/Weather/cumulonimbus_small1.xml";} else {path = "Models/Weather/cumulonimbus_small2.xml";} } } else if (type == "Cirrus") { if (subtype == "small") { if (rn > 0.5) {path = "Models/Weather/cirrus1.xml";} else {path = "Models/Weather/cirrus2.xml";} } else if (subtype == "large") { if (rn > 0.5) {path = "Models/Weather/cirrus1.xml";} else {path = "Models/Weather/cirrus2.xml";} } } else if (type == "Cirrocumulus") { if (subtype == "small") { if (rn > 0.5) {path = "Models/Weather/cirrocumulus1.xml";} else {path = "Models/Weather/cirrocumulus2.xml";} } else if (subtype == "large") { if (rn > 0.5) {path = "Models/Weather/cirrocumulus1.xml";} else {path = "Models/Weather/cirrocumulus2.xml";} } } else if (type == "Nimbus") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/nimbus_sls1.xml";} else if (rn > 0.6) {path = "Models/Weather/nimbus_sls2.xml";} else if (rn > 0.4) {path = "Models/Weather/nimbus_sls3.xml";} else if (rn > 0.2) {path = "Models/Weather/nimbus_sls4.xml";} else {path = "Models/Weather/nimbus_sls5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/nimbus_sl1.xml";} else if (rn > 0.6) {path = "Models/Weather/nimbus_sl2.xml";} else if (rn > 0.4) {path = "Models/Weather/nimbus_sl3.xml";} else if (rn > 0.2) {path = "Models/Weather/nimbus_sl4.xml";} else {path = "Models/Weather/nimbus_sl5.xml";} } } else if (type == "Stratus") { if (subtype == "small") { if (rn > 0.8) {path = "Models/Weather/stratus_layer1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_layer2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_layer3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_layer4.xml";} else {path = "Models/Weather/stratus_layer5.xml";} } else if (subtype == "large") { if (rn > 0.8) {path = "Models/Weather/stratus_layer1.xml";} else if (rn > 0.6) {path = "Models/Weather/stratus_layer2.xml";} else if (rn > 0.4) {path = "Models/Weather/stratus_layer3.xml";} else if (rn > 0.2) {path = "Models/Weather/stratus_layer4.xml";} else {path = "Models/Weather/stratus_layer5.xml";} } } else {print("Cloud type ", type, " subtype ",subtype, " not available!");} return path; } ########################################################### # place a single cloud ########################################################### var create_cloud = func(type, path, lat, long, alt, heading, rain_flag) { var n = props.globals.getNode("local-weather/clouds", 1); var cloud_number = n.getNode("cloud-number").getValue(); for (var i = cloud_number; 1; i += 1) if (n.getChild("cloud", i, 0) == nil) break; cl = n.getChild("cloud", i, 1); var m = props.globals.getNode("models", 1); for (var i = cloud_number; 1; i += 1) if (m.getChild("model", i, 0) == nil) break; model = m.getChild("model", i, 1); cl.getNode("type", 1).setValue(type); var latN = cl.getNode("position/latitude-deg", 1); latN.setValue(lat); var lonN = cl.getNode("position/longitude-deg", 1); lonN.setValue(long); var altN = cl.getNode("position/altitude-ft", 1); altN.setValue(alt); var hdgN = cl.getNode("orientation/true-heading-deg", 1); hdgN.setValue(heading); var pitchN = cl.getNode("orientation/pitch-deg", 1); pitchN.setValue(0.0); var rollN = cl.getNode("orientation/roll-deg", 1);rollN.setValue(0.0); #cl.getNode("velocities/true-airspeed-kt", 1).setValue(kias); #cl.getNode("velocities/vertical-speed-fps", 1).setValue(0.0); #cl.getNode("position/trafo-flag", 1).setValue(1); model.getNode("path", 1).setValue(path); model.getNode("legend", 1).setValue("Cloud"); model.getNode("latitude-deg-prop", 1).setValue(latN.getPath()); model.getNode("longitude-deg-prop", 1).setValue(lonN.getPath()); model.getNode("elevation-ft-prop", 1).setValue(altN.getPath()); model.getNode("heading-deg-prop", 1).setValue(hdgN.getPath()); model.getNode("pitch-deg-prop", 1).setValue(pitchN.getPath()); model.getNode("roll-deg-prop", 1).setValue(rollN.getPath()); model.getNode("load", 1).remove(); n.getNode("cloud-number").setValue(n.getNode("cloud-number").getValue()+1); } ########################################################### # clear all clouds and effects ########################################################### var clear_all = func { # clear the clouds and models var cloudNode = props.globals.getNode("local-weather/clouds", 1); cloudNode.removeChildren("cloud"); var modelNode = props.globals.getNode("models", 1).getChildren("model"); foreach (var m; modelNode) { var l = m.getNode("legend").getValue(); if (l == "Cloud") { m.remove(); } } cloudNode.getNode("cloud-number",1).setValue(0); # clear effect volumes props.globals.getNode("local-weather/effect-volumes", 1).removeChildren("effect-volume"); # stop the effect loop and the interpolation loop, make sure thermal generation is off setprop(lw~"effect-loop-flag",0); setprop(lw~"interpolation-loop-flag",0); setprop(lw~"tmp/generate-thermal-lift-flag",0); # also remove rain and snow effects setRain(0.0); setSnow(0.0); } ########################################################### # place a cumulus system ########################################################### var create_cumosys = func (blat, blon, balt, nc, size) { var path = "Models/Weather/blank.ac"; var i = 0; var p = 0.0; var rn = 0.0; var place_lift_flag = 0; var sec_to_rad = 2.0 * math.pi/86400; calc_geo(blat); # get the local time of the day in seconds var t = getprop("sim/time/utc/day-seconds"); t = t + getprop("sim/time/local-offset"); # print("t is now:", t); # and make a simple sinusoidal model of thermal strength var t_factor1 = 0.5 * 1.0-math.cos((t * sec_to_rad)); var t_factor2 = 0.5 * 1.0-math.cos((t * sec_to_rad)-0.52); # print("t-factor is now: ",t_factor); nc = t_factor1 * nc * math.cos(blat/180.0*math.pi); while (i < nc) { p = 0.0; place_lift_flag = 0; var x = (2.0 * rand() - 1.0) * size; var y = (2.0 * rand() - 1.0) * size; var lat = blat + y * m_to_lat; var lon = blon + x * m_to_lon; var info = geodinfo(lat, lon); if (info != nil) { if (info[1] != nil){ var landcover = info[1].names[0]; if (contains(landcover_map,landcover)) {p = p + landcover_map[landcover];} else {print(p, " ", info[1].names[0]);} }} if (rand() < p) { if (rand() + (2.0 * p) > 1.0) { path = select_cloud_model("Cumulus","large"); place_lift_flag = 1; } else {path = select_cloud_model("Cumulus","small");} create_cloud("Cumulus", path, lat, lon, balt, 0.0, 0); # now see if we need to create a thermal - first check the flag if (getprop(lw~"tmp/generate-thermal-lift-flag") == 1) { # now check if convection is strong if (place_lift_flag == 1) { var lift = 1.0 + 20.0 * p * rand(); var radius = 300 + 300 * rand(); create_effect_volume(1, lat, lon, radius, radius, 0.0, 0.0, balt+500.0, -1, -1, -1, -1, lift, 1); } # end if place_lift_flag } # end if generate-thermal-lift-flag } # end if rand < p i = i + 1; } # end while } ########################################################### # terrain sampling ########################################################### var terrain_presampling = func { var size = 15000.0; var blat = getprop("position/latitude-deg"); var blon = getprop("position/longitude-deg"); var elevation = 0.0; var n=[]; setsize(n,20); calc_geo(blat); for(j=0;j<20;j=j+1){n[j]=0;} for (i=0; i<1000; i=i+1) { var x = (2.0 * rand() - 1.0) * size; var y = (2.0 * rand() - 1.0) * size; var lat = blat + y * m_to_lat; var lon = blon + x * m_to_lon; var info = geodinfo(lat, lon); if (info != nil) {elevation = info[0] * m_to_ft;} for(j=0;j<20;j=j+1){if (elevation < 500.0 * (j+1)) {n[j] = n[j]+1; break;}} } for (i=0;i<20;i=i+1){print(500.0*i," ",n[i]);} } ########################################################### # place a barrier cloud system ########################################################### var create_rise_clouds = func (blat, blon, balt, nc, size, winddir, dist) { var path = "Models/Weather/blank.ac"; var i = 0; var p = 0.0; var rn = 0.0; var nsample = 10; var counter = 0; var elevation = 0.0; var dir = (winddir + 180.0) * math.pi/180.0; var step = dist/nsample; calc_geo(blat); while (i < nc) { counter = counter + 1; p = 0.0; elevation=0.0; var x = (2.0 * rand() - 1.0) * size; var y = (2.0 * rand() - 1.0) * size; var lat = blat + y * m_to_lat; var lon = blon + x * m_to_lon; var info = geodinfo(lat, lon); if (info != nil) {elevation = info[0] * m_to_ft;} if ((elevation < balt) and (elevation != 0.0)) { for (var j = 0; j balt) { p = 1.0 - j * (1.0/nsample); break; } } } } if (counter > 500) {print("Cannot place clouds - exiting..."); i = nc;} if (rand() < p) { path = select_cloud_model("Altocumulus","large"); #print("Cloud ",i, " after ",counter, " tries"); create_cloud("Altocumulus", path, lat, lon, balt, 0.0, 0); counter = 0; i = i+1; } } # end while } ########################################################### # place a cloud streak ########################################################### var create_streak = func (type, blat, blong, balt, nx, xoffset, edgex, ny, yoffset, edgey, direction, tri) { var flag = 0; var path = "Models/Weather/blank.ac"; calc_geo(blat); var dir = direction * math.pi/180.0; var ymin = -0.5 * ny * yoffset; var xmin = -0.5 * nx * xoffset; var xinc = xoffset * (tri-1.0) /ny; var jlow = int(nx*edgex); var ilow = int(ny*edgey); for (var i=0; i(nx-jlow-1))) and ((i(ny-ilow-1)))) # select a small or no cloud { if (rn > 2.0) {flag = 1;} else {path = select_cloud_model(type,"small");} } if ((j(nx-jlow-1)) or (i(ny-ilow-1))) { if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"small");} } else { # select a large cloud if (rn > 5.0) {flag = 1;} else {path = select_cloud_model(type,"large");} } #var label = get_label(type); if (flag==0){create_cloud("tmp_cloud", path, lat, long, balt, 0.0, 0);} } } } ########################################################### # place a cloud layer ########################################################### var create_layer = func (type, blat, blon, balt, bthick, rx, ry, phi, density, edge, rainflag, rain_density) { var i = 0; var area = math.pi * rx * ry; var circ = math.pi * (rx + ry); # that's just an approximation var n = int(area/80000000.0 * 100 * density); var m = int(circ/63000.0 * 40 * rain_density); var path = "Models/Weather/blank.ac"; phi = phi * math.pi/180.0; if (contains(cloud_vertical_size_map, type)) {var alt_offset = cloud_vertical_size_map[type]/2.0 * m_to_ft;} else {var alt_offset = 0.0;} while(i ((1.0 - edge) * (1.0- edge))) { if (rand() > 0.4) { path = select_cloud_model(type,"small"); create_cloud(type, path, lat, lon, alt, 0.0, 0); } } else { path = select_cloud_model(type,"large"); create_cloud(type, path, lat, lon, alt, 0.0, 0); } i = i + 1; } } i = 0; if (rainflag ==1){ while(i