1
0
Fork 0

Local weather v0.81 by Thorsten Renk

This commit is contained in:
Frederic Bouvier 2010-08-03 08:18:14 +02:00
parent c2450d1ccc
commit 4dd0ef9a08
11 changed files with 3156 additions and 869 deletions

View file

@ -7,7 +7,7 @@
<body>
<h1>Local Weather Package - v0.7</h1>
<h1>Local Weather Package - v0.8</h1>
<h2>1. Introduction</h2>
@ -15,21 +15,22 @@ The aim of a local weather system is to simulate weather phenomena tied to speci
This is in contrast to the current (v.2.0.0) weather system of Flightgear where weather changes affect the weather everywhere in the simulated world and are (with few exceptions) not tied to specific locations. In such a system, it is impossible to observe e.g. the approach of a rainfront while flying in sunshine.<p>
The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.7, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and event volumes. The package contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution. For long-range flights, it automatically provides transitions between different weather patterns like fronts and low and high pressure areas. However, basically all features currently present can and will eventually be improved.<p>
The local weather package ultimately aims to provide the functionality to simulate such local phenomena. In version 0.8, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and event volumes. The dynamics of the different systems is tied together - clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution. Unfortunately, as of v0.8, there is no interaction yet between the windfield and the cloud-generating algorithms, i.e. while the placement algorithms create a realistic configuration of thermals and convective clouds, the wind will simply move this configuration, not create, destroy or move clouds in altitude dynamically (which would be realistic). <p>
For long-range flights, the system automatically provides transitions between different weather patterns like fronts and low and high pressure areas. However, basically all features currently present can and will eventually be improved.<p>
As of version 0.7, the system does not contain dynamics (development of convective clouds, wind displacement of clouds...). Since wind and dynamics are closely related, any wind parameters can currently <i>not</i> be controlled from the local weather algorithms.<p>
<h2>2. Installation</h2>
The package needs to be unpacked in the Flightgear root directory. It writes content into the <i>Nasal/, gui/, gui/dialogs/, Shaders, Effects/, Docs/</i>, and <i>Models/Weather/</i> subdirectories. The installation does not overwrite any of the default Flightgear files, but to be accessible from the menu, one must copy <i>gui/menubar.xml.alt</i> to the default <i>menubar.xml</i> or copy the last two lines of the environemnt menu calling <i>local_weather</i> and <i>local_weather_tiles</i> into the default file.<p>
This adds the items <i>Local Weather</i> and <i>Local weather tiles</i> to the <i>Environment</i> menu when Flightgear is up. The central functionality is contained in <i>local_weather.nas</i> which is loaded at startup and identifies itself with a message, but does not start any functions unless called from the GUI.<p>
This adds the items <i>Local Weather</i> and <i>Local weather tiles</i> to the <i>Environment</i> menu when Flightgear is up. Most of the basic functionality is contained in <i>local_weather.nas</i> which is loaded at startup and identifies itself with a message, but does not start any functions unless called from the GUI.<p>
<h2>3. Functionality</h2>
The general rule is that the gui is not hardened against problematic user input, for example it will not reject meaningless input. It is recommended to watch the console, because some level of warnings and errors are passed to the console. Placement calls may sometimes take a significant time to execute especially for large numbers of clouds tied in a complicated way to the terrain. Placing 500 barrier clouds against a small barrier may take a minute to compute.<p>
The general rule is that the gui is not hardened against problematic user input, for example it will not reject meaningless input like negative windspeeds or unphysical windshear. It is recommended to watch the console, because some level of warnings and errors are passed to the console. Placement calls may sometimes take a significant time to execute especially for large numbers of clouds tied in a complicated way to the terrain. Placing 500 barrier clouds against a small barrier may take a minute to compute. During this time, a reduced framerate is to be expected<p>
The first menu contains the low level cloud placement functions. Its purpose is mainly for developing cloud patterns without having to resort to re-type the underlying Nasal code every time. Currently four options are supported: <i>Place a single cloud</i>, <i>Place a cloud streak</i>, <i>Start the convective system</i>, <i>Create barrier clouds</i> and <i>Place a cloud layer</i>.<p>
The first menu contains the low level cloud placement functions. Its purpose is mainly for developing cloud patterns without having to resort to re-type the underlying Nasal code every time. Currently five options are supported: <i>Place a single cloud</i>, <i>Place a cloud streak</i>, <i>Start the convective system</i>, <i>Create barrier clouds</i> and <i>Place a cloud layer</i>.<p>
<center>
<img src="menu1.jpg">
@ -89,21 +90,38 @@ The second menu is used to place complete weather tiles based on low-level calls
<img src="menu2.jpg">
</center><p>
The dropdown menu is used to select the type of weather tile to build. The menu contains two groups of tiles - the first six are classified by airmass, whereas the last two are scenarios intended for soaring. In addition, two parameters can be entered. The first is the tile orientation. Some tiles, most notably incoming fronts, have a direction along which the weather changes. The tiles are set up in such a way that fronts come from north, changing orientation rotates the whole tile to the specified direction. As soon as wind is implemented in the local weather system, the tile orientation will essentially also govern the wind direction (clearly, there is a relation between from where a front comes and the wind direction). Currently, the functionality of tile orientation is there, but mostly untested and at the moment not particularly useful.<p>
The dropdown menu is used to select the type of weather tile to build. The menu contains two groups of tiles - the first are classified by airmass, whereas the last two are scenarios intended for soaring. <p>
The second parameter, the altitude offset, is to manually adjust the altitude level of clouds in the absence of terrain presampling. Cloud layer placement calls are then specified for absolute altitudes and calibrated at sea level. As a result, layers are placed too low in mountainous terrain, hence the need for an offset. <p>
Below are entries for three parameters. The first two are the simplified version of wind direction and speed for the user who is not interested in specifying many different wind interpolation points.
The third parameter, the altitude offset, is to manually adjust the altitude level of clouds in the absence of terrain presampling. Cloud layer placement calls are then specified for absolute altitudes and calibrated at sea level. As a result, layers are placed too low in mountainous terrain, hence the need for an offset. The offset may at present also be useful for dynamical weather, as convective clouds with terrain presampling follow terrain altitude, which looks strange when the clouds are allowed to drift in the wind without altitude correction. <p>
The dropdown menu for the wind contains various models for how the windfield is specified which require a different amount of user-specified input. The options are described further down when the windfield modelling is described in more detail.<p>
The dropdown menu for the tile selection mode controls the long-range behaviour of weather. It specifies according to what rules tiles are automatically generated once the aircraft reaches the border of the original tile. The option 'single tile' creates a single weather tile as specified without automatic generation of further tiles. The option 'repeat tile' creates new tiles of the same type as the originally selected tile. This does not mean that weather will be unchanged during flight, as both parameters like pressure, temperature and visibility as well as the positioning of cloud banks are randomized to some degree. In addition, each tile typically contains 2-5 different cloud scenarios, so five repeated generations of 'low-pressure-border' tiles may never result in the same arrangement of cloud layers. Necertheless, the option will keep weather conditions roughly the same. This is different with the (somewhat optimistically named) 'realistic weather'. This option allows transitions between different airmasses, thus one may select 'low-pressure-core' initially, but as the flight goes on, eventually a region of high pressure and clear skies may be reached. Currently this change between airmasses does not include transitions across fronts. Moreover, it does not cover transitions to arctic or tropical weather conditions - those will be covered in a future release. Note that 'realistic weather' does not work for the two soaring scenarios, 'repeat tile' does not work for any tile which is part of a front.<p>
The final option, 'METAR', generates weather according to parsed METAR information. This information must be made available in the property tree. Currently this is <b>not</b> done automatically and the METAR system does <b>not</b> work with real-weather-fetch, this needs some work on the Flightgear core.<p>
Below the menu are four tickboxes. 'Terrain presampling' finds the distribution of altitude in the terrain before placing a cloud layer. As a result, the layers or clouds are automatically placed at the correct altitude above ground in level terrain. In mountain regions, cloud placement is fairly tricky, and the algorithm analyzes quantities like the median altitude to determine what to do. The appendix contains a detailed description of the algorithm. If the box is ticked, the altitude offset specified above is not parsed.<p>
Below the menu are five tickboxes. 'Terrain presampling' finds the distribution of altitude in the terrain before placing a cloud layer. As a result, the layers or clouds are automatically placed at the correct altitude above ground in level terrain. In mountain regions, cloud placement is fairly tricky, and the algorithm analyzes quantities like the median altitude to determine what to do. The appendix contains a detailed description of the algorithm. If the box is ticked, the altitude offset specified above is not parsed.<p>
'Worker threads' is an option to distribute the work flow. Usually, the local weather package will compute a task till it is done before starting the next. Thus, creating a new weather tile may lead to a few seconds freeze, before Flightgear continues normally. With 'worker threads' selected, computations will be split across several frames. The advantage is that Flightgear stays responsive during loading and unloading of weather tiles, and in general the flight continues smoothly, albeit with reduced framerate. However, selecting this option does not guarantee that an operation is finished by the time another is beginning - thus there may be situations in which the loading of a new tile blocks unloading of an old one and so on, in essence leading to processes competing for access to the weather array, resulting in an extended period of very low framerates. Dependent on system performance, this may or may not be acceptable to the user. 'asymmetric range' is an experimental performance-improving option (see below). Finally, 'detailed clouds' will change the technique for generating Cumulus clouds from a multilayer model to multiple cloudlets filling a box. This improves the visual appearance of the clouds significantly, albeit at the expense of a (significant) loss of framerate. Rendering multiple tiles of dense Cumulus development with detailed clouds will quite possibly freeze even a powerful system. <p>
The option 'dynamical weather' ties all clouds and weather effects to the windfield. If that option is not chosen, the wind is still generated according to the chosen model, but only felt by the aircraft. This makes e.g. soaring unrealistic, as the aircraft continuously drifts out of a static thermal below a static cap cloud. When 'dynamical weather' is selected, aircraft, cloud and thermal are all displaced by the wind.<p>
The slider 'Thermal properties' is only relevant for soaring scenarios. It governs the rato of maximum lift to radius of a thermal. A setting close to 'low convection' creates large thermals with relatively small lift and virtually no turbulence, a setting close to 'rough day' creates very narrow, turbulent thermals with large lift. Unless thermals are placed, no other weather tile is affected by the settings.<p>
The button 'Show winds' brings up the detailed wind menu which is needed for the wind models 'aloft interpolated' and 'aloft waypoints':<p>
<center>
<img src="menu3.jpg">
</center><p>
For 'aloft interpolated', the menu is used by inserting wind direction and speed for all given altitudes. After 'Okay', the specified values are used. For 'aloft waypoints', the same info must be supplied for a series of waypoints. First, the latitude and longitude has to be inserted, afterwards the aloft winds for that point below. The button 'Set waypoint' commits the windfield as specified in the menu for this position into memory. For orientation, the number of points inserted is counted on the lower right. 'Clear Waypoints' removes all information entered so far. Note that 'Okay' does not commit the data for a waypoint. <p>
In principle, the waypoint information inserted so far can be seen using the property browser. It is stored under <i>/local-weather/interpolation/wind[n]/</i>.<p>
The following pictures show the results of tile setups 'Low-pressure-border' and 'High-pressure-border':<p>
<center>
@ -168,9 +186,34 @@ Effect volumes are always specified between a minimum and a maximum altitude, an
where <i>geometry</i> is a flag (1: circular, 2: elliptical and 3: rectangular), <i>lat</i> and <i>lon</i> are the latitude and longitude, <i>r1</i> and <i>r2</i> are the two size parameters for the elliptic or rectangular shape (for the circular shape, only the first is used), <i>phi</i> is the rotation angle of the shape (not used for circular shape), <i>alt_low</i> and <i>alt_high</i> are the altitude boundaries, <i>vis, rain, snow, turb</i> and <i>lift</i> are weather parameters which are either set to the value they should assume, or to -1 if they are not to be used, or to -2 if a function instead of a parameter is to be used. Since thermal lift can be set to negative values in a sink, a separate flag is provided in this case.<p>
In version 0.61, thermal lift is implemented by function. There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.
In version 0.8, thermal lift is implemented by function. There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.
<h2>6. Property tree structure</h2>
<h2>6. Wind models and dynamical weather</h2>
In reality, the wind is a vector field changing in space and time, subject to physical boundary conditions such as a continuity equation (there are no sources or sinks of air - air flowing out of a volume element must be balanced by air flowing into the volume element). This vector field has two horizontal and one vertical component. <p>
It is quite clear that the wind model within local weather has to be an approximation to the real situation. First, the windfield is assumed to always have horizontal components only - vertical air movement is simulated on top of the wind field by ridge lift generated from the Flightgear core and by thermals and turbulence as effect volumes. <p>
In the horizontal windfield, aloft and bounday layers need to be distinguished. The aloft wind layers are high enough so that they do not interact with the terrain (and hence can be specified as a function of altitude above sea level), the low region where the aloft winds experience friction by interaction with the terrain and are slowed down constitutes the boundary layer. The boundary layer hence needs to be defined as a function of altitude above ground - it shifts as the elevation shifts. The size of the boundary layer, as well as its capacity to slow down aloft winds, depends on the roughness of the terrain. Over the open sea, the boundary layer is typically as small as 150 ft, while over rough terrain it can extend up to several hundred ft.<p>
When the option 'dynamical weather' is active, clouds and effect volumes move with the wind. Due to performance reasons, only clouds in the field of view are processed in each frame. As an efficient way to do this, a quadtree structure is used. However, this has the side effect that all clouds inside a tile need to be moved with the same windspeed (otherwise they would over time drift out of the position where the quadtree expects to find them). Since thermals and their cap clouds should not drift apart, also weather effects are moved with the same windspeed inside each tile. In the following, this is referred to as 'tile wind speed'. The tile wind speed always corresponds to the lowest aloft layer windspeed. The reason why this is considered acceptable is that at the same altitude for different positions inside a tile, the correct windspeed is at most a few kt different from the tile windspeed, and this is impossible to see visually. At high altitudes, the true wind is very different from the speed at which clouds are moved, but without reference and from fast-moving planes, the error is again very hard to see. However, with e.g. a hot air balloon, the fact that at high altitudes clouds are not moved with the high-altitude windspeed would be quite apparent.<p>
Dependent on how detailed the wind field should be specified, what the pilot aims to do and how much user control is desired, there are several options to model the wind.
<ul>
<li> <b>constant</b> sets the aloft wind to the same speed and direction as specified in the weather tile menu everywhere - at every spatial position and at every altitude. This is for the casual pilot who just wants some simple wind setting, or when it is mandatory that clouds, plane and weather effects all move with the same speed, such as for lighter-than-air aviation. Note that the wind set in the menu is not the wind seen on the runway, as the option sets the aloft wind, from which the boundary layer wind is computed using terrain information.<p>
<li> <b>constant in tile</b> keeps the wind equal to the tile windspeed, but allows to change randomly direction and strength a bit between tiles, so that the wind at the destination will not be equal to the wind at takeoff, but such that still the same wind is felt by aircraft, clouds and weather effects. The drawback of this option is that the wind changes discontinuously as a tile boundary is crossed (felt as a sudden gust) and that aloft winds do not change in altitude This model is suitable e.g. for gliders, when it is important that glider, thermal and cap cloud move with the same speed, high altitudes are out of reach anyway and a little variation in the wind is okay.<p>
<li> <b>aloft interoplated</b> requires the wind menu. Here, the wind is kept constant as a function of position, but is allowed to vary in altitude according to the information provided by the user for the different pressure altitudes. A linear interpolation in altitude is used. The option essentially provides the functionality of the default Flightgear weather conditions menu.<p>
<li> <b>aloft waypoints</b> finally allows to interpolate the windfield in altitude and in position according to the grid of interpolation points (or 'waypoints') entered by the user in the wind menu. The correct tile windspeed and direction is computed by the same interpolation. A linear interpolation in altitude and an inverse distance weighting for the interpolation in position are used. This is a suitable option for e.g. airliner operation when high altitude wind maps are available.<p>
</ul>
In all cases, the boundary layer is computed separately. Since the boundary layer effect depends on terrain, there is no direct way to set the wind as experienced on the runway (but of course changing the lowest aloft layer will lead to the desired result).
<h2>7. Property tree structure</h2>
The internal state of the local weather system is found in the property tree under <i>local-weather/</i>. In this directory, various loop flags are found. They indicate the state of the main monitoring loops - they are set to 1 if a loop is running, setting them to zero terminates the loop.<p>
@ -178,13 +221,13 @@ The <i>local-weather</i> folder contains various subdirectories. <i>clouds/</i>
The <i>local-weather/effect-volumes/</i> subfolder contains the management of the effect volumes. It has the total count of specified effect volumes, along with the count of currently active volumes for each property. If volumes are defined, their properties are stored under <i>local-weather/effect-volumes/effect-volume[i]/</i>. In each folder, there are <i>position/</i> and <i>volume/</i> storing the spatial position and extent of the volume, as well as the <i>active-flag</i> which is set to 1 if the airplane is in the volume and the <i>geometry</i> flag which determines if the volume has circular, elliptical or rectangular shape. Finally, the <i>effects/</i> subfolder holds flags determining of a property is to be set when the volume is active and the corresponding values. On entry, the effect volumes also create a subfolder <i>restore/</i> in which the conditions as they were when the volume was entered are saved.<p>
<i>local-weather/interpolation/</i> holds all properties which are set by the interpolation system, as well as subfolders <i>station[i]/</i> in which the weather station information for the interpolation are found. Basically, here is the state of the weather as it is outside of effect volumes. Since parameters may be set to different values in effect volumes, the folder <i>local-weather/current/</i> contains the weather as the local weather system currently thinks it should be. Currently, weather is actually passed to the Flightgear environment system through several workarounds. In a clean C++ supported version, the parameters should be read from here.<p>
<i>local-weather/interpolation/</i> holds all properties which are set by the interpolation system, as well as subfolders <i>station[i]/</i> in which the weather station information for the interpolation are found and subfolders <i>wind[i]</i> where wind information in the case of 'aloft interpolated' or 'aloft waypoints' is stored. Basically, here is the state of the weather as it is outside of effect volumes. Since parameters may be set to different values in effect volumes, the folder <i>local-weather/current/</i> contains the weather as the local weather system currently thinks it should be. Currently, weather is actually passed to the Flightgear environment system through several workarounds. In a clean C++ supported version, the parameters should be read from here.<p>
<i>local-weather/tiles</i> stores the information of the 9 managed weather tiles (the one the airplane is currently in, and the 8 surrounding it). By default each directory contains the tile center coordinates and a flag if it has been generated. Tiles are not generated unless a minimum distance to the tile center has been reached. Once this happens, the tile type is written as a code, and the cloud, interpolation and effect volume information corresponding to the tile is generated. <p>
<i>local-weather/METAR</i> is used to store the METAR information for the METAR based tile setup. This must include latitude, longitude and altitude of a weather station, temperature, dewpoint, pressure, wind direction and speed, rain, snow and thunderstorm information as well as cloud layer altitude and coverage in oktas. Any properties set here will be executed when 'METAR' is selected as tile selection mode as long as <i>local-weather/METAR/available-flag</i> is set to 1 (this flag is set to zero whenever a tile has been created from METAR info to indicate that the information has been used, so if the user wants to reuse the information, then the flag must be reset).<p>
<h2>7. Weather tile setup</h2>
<h2>8. Weather tile setup</h2>
Examples for weather tile setup can be found in <i>Nasal/weather-tiles.nas</i>. Each tile is generated by a sequence of Nasal function calls to first set weather stations, then to draw the cloud layers, and effect volumes. It is a bit awkward to have to write in Nasal to customize the system, but I can't think of a reasonable GUI for the task, except marking every placement on a map which exceeds my coding skills a bit. The problem is that there is no real standard solution - every weather tile needs different calls, sometimes a large one generating many clouds, sometimes several smaller ones.<p>
@ -198,35 +241,47 @@ If the cloud layer has an orientation, then all placement coordinates should be
To make your own tile visible, an entry in the menu <i>gui/dialogs/local_weather_tiles.xml</i> needs to be created and the name needs to be added with a tile setup call to the function <i>set_tile</i> in <i>Nasal/local_weather.nas</i>.
<h2>8. Performance tuning</h2>
<h2>9. Performance tuning</h2>
With default settings, the local weather package generates a 40x40 km weather tile when the aircraft is closer than 35 km to the tile center and unloads it when the aircraft is more than 37 km away. This means that the system can generate at most 4 tiles at once and clouds are visible for at least 15 km and up to 30 km (the latter number determined by fading in the shaders). However, rendering and managing multiple overcast cloud layers in a region of 80x80 km is a significant drain on performance. For older systems, a few things can be tried:<p>
* the menu option 'asymmetric range' decreases loading and unloading ranges in a 45 degree sector behind the aircraft. This means that in straight flight, less tiles will be loaded at the same time, as tiles in the rear are unloaded more quickly. The option is currently experimental.<p>
<ul>
* a further reduction in the amount of simultaneously generated tiles can be achieved by changing the ranges. These are exposed in the property tree as <i>local-weather/config/distance-to-load-tile-m</i> and <i>local-weather/config/distance-to-remove-tile-m</i>. Note that the removal range <b>must</b> be larger than the loading range - otherwise just generated tiles will be immediately unloaded! Ranges below 20 km will guarantee that only one tile is loaded at a time, but will create empty spots when no tile is loaded. A range above 28 km will guarantee that the aircraft never flies in an empty tile, but empty sky in front will be visible. Finally, ranges above 56 km guarantee that all 9 tiles (a region of 120x120 km) are managed at all times - but will most likely cause a severe drop in framerate for most scenarios.<p>
<li> the menu option 'asymmetric range' decreases loading and unloading ranges in a 45 degree sector behind the aircraft. This means that in straight flight, less tiles will be loaded at the same time, as tiles in the rear are unloaded more quickly. The option is currently experimental.<p>
* if this does not help, try avoiding scenarios with large cloud count. As a rule, low pressure areas have high cloud count, high pressure areas have a low cloud count. Do not use 'detailed clouds', which tend to generate large cloud counts.<p>
<li> a further reduction in the amount of simultaneously generated tiles can be achieved by changing the ranges. These are exposed in the property tree as <i>local-weather/config/distance-to-load-tile-m</i> and <i>local-weather/config/distance-to-remove-tile-m</i>. Note that the removal range <b>must</b> be larger than the loading range - otherwise just generated tiles will be immediately unloaded! Ranges below 20 km will guarantee that only one tile is loaded at a time, but will create empty spots when no tile is loaded. A range above 28 km will guarantee that the aircraft never flies in an empty tile, but empty sky in front will be visible. Finally, ranges above 56 km guarantee that all 9 tiles (a region of 120x120 km) are managed at all times - but will most likely cause a severe drop in framerate for most scenarios.<p>
* a drastic solution is to set the visual ranges lower. Currently, cloud models are set to be visible up to 30 km. Changing the visibility range in the range animation of all cloud model xml wrappers will improve performance accordingly. To achieve a nice fading into the background instead of a sudden disappearance, it is recommended to adjust the visibility range in the shaders accordingly. It would probably be good to expose the visual range as a property, but currently it's not yet done, as passing a property to the shader requires Flightgear CVS and cannot be done in 2.0.0.<p>
<li> if this does not help, try avoiding scenarios with large cloud count. As a rule, low pressure areas have high cloud count, high pressure areas have a low cloud count. Do not use 'detailed clouds', which tend to generate large cloud counts.<p>
<li> a drastic solution is to set the visual ranges lower. Currently, cloud models are set to be visible up to 30 km. Changing the visibility range in the range animation of all cloud model xml wrappers will improve performance accordingly. To achieve a nice fading into the background instead of a sudden disappearance, it is recommended to adjust the visibility range in the shaders accordingly. It would probably be good to expose the visual range as a property, but currently it's not yet done, as passing a property to the shader requires Flightgear CVS and cannot be done in 2.0.0.
Performance for overcast layers currently is a limiting issue and there are a few ideas around how to improve it - dependent if these work or not, future releases may work better.<p>
* a different issue is a characteristic small pause every 3 seconds. This is caused by the interpolation loop resetting the weather parameters. Currently, a computationally expensive workaround is needed to do so, causing the problem. Work on a better environment controller is on the way, however until that modification to the core Flightgear code is implemented, the best solution is to set the loop time in <i>Nasal/local-weather.nas</i> to a larger value. <p>
<li> a different issue is a characteristic small pause every second. This is caused by the interpolation loop resetting the weather parameters. Currently, a computationally expensive workaround is needed to do so, causing the problem. Work on a better environment controller is on the way, however until that modification to the core Flightgear code is implemented, the best solution is to set the loop time in <i>Nasal/local-weather.nas</i> to a larger value. <p>
<h2>9. Known issues</h2>
<li> dynamical weather uses a lot of performance. If framerate is low and you don't need it, don't use it! From fast planes, cloud drift is almost impossible to see against the relative motion of cloud and airplane anyway.<p>
* The local weather system does not mix well with the standard weather system. 3d cloud layers can be placed in the absence of effect volumes, but any effect volume causing precipitation will let the layer behave in a strange way. Likewise, 2d cloud layers can be placed, but may or may not lead to strange rendering artefacts. Local weather, as long as interpolation and effect volumes are running, will in general overwrite all other settings - bother real weather METAR and user-specified settings from the menu. The results of mixing standard and local weather settings are unpredictable, and may not lead to the desired results.<p>
</ul>
* Some cloud textures have artefacts, rain textures have too sharp boundaries and in general some things could look better. Please don't complain, but rather get me good photographs of the sky, cloud texture files or create better AC3D cloud models. I will eventually improve texture quality, but it's not high up in the to-do list, and the cloud model files are openly accessible for anyone with an editor.<p>
<h2>10. Known issues</h2>
* Rain and snow may not start properly. For me, rain is only generated when I switch 'Shader effects' on and off in the menu on startup, otherwise neither moving the menu slider nor entering an effect volume generate rain. This seems to be a bug of some Flightgear versions, not of the local weather system.<p>
<ul>
* Especially with multiple overcast layers and weather fronts, loading and unloading weather tiles may take a long time / cause severe drops in framerate. Please refer to performance tuning to solve such problems. In general, overcast layers and tropical weather tiles do require a system on the high end of the performance scale to render properly.<p>
<li> The local weather system does not mix well with the standard weather system. 3d cloud layers can be placed in the absence of effect volumes, but any effect volume causing precipitation will let the layer behave in a strange way. Likewise, 2d cloud layers can be placed, but may or may not lead to strange rendering artefacts. Local weather, as long as interpolation and effect volumes are running, will in general overwrite all other settings - bother real weather METAR and user-specified settings from the menu. The results of mixing standard and local weather settings are unpredictable, and may not lead to the desired results.<p>
* The local weather package is able to occasionally trigger errors like 'Warning:: Picked up error in TriangleIntersect'. These seem to be a problem in the core Flightgear code - the package does nothing but placing normal (rather simple) AC3D models into the scenery.<p>
<li> Some cloud textures have artefacts, rain textures have too sharp boundaries and in general some things could look better. Please don't complain, but rather get me good photographs of the sky, cloud texture files or create better AC3D cloud models. I will eventually improve texture quality, but it's not high up in the to-do list, and the cloud model files are openly accessible for anyone with an editor.<p>
* The thermals in the soaring scenarios need a CVS patch to work.<p>
<li> Rain and snow may not start properly. For me, rain is only generated when I switch 'Shader effects' on and off in the menu on startup, otherwise neither moving the menu slider nor entering an effect volume generate rain. This seems to be a bug of some Flightgear versions, not of the local weather system.<p>
<li> Especially with multiple overcast layers and weather fronts, loading and unloading weather tiles may take a long time / cause severe drops in framerate. Please refer to performance tuning to solve such problems. In general, overcast layers and tropical weather tiles do require a system on the high end of the performance scale to render properly.<p>
<li> The local weather package is able to occasionally trigger errors like 'Warning:: Picked up error in TriangleIntersect'. These seem to be a problem in the core Flightgear code - the package does nothing but placing normal (rather simple) AC3D models into the scenery.<p>
<li> For dynamical weather, clouds sometimes appear to 'jump' to a position. The reason is that the control loop of cloud drift accepts for performance reasons only a limited number of clouds. If there are more in the field of view, the most distant clouds are not processed. As nearby clouds drift out of the visual field, more distant clouds move into the loop, at which point their position is suddenly updated, resulting in a jump. Short of committing potentially vast computational resources (if there is a large number of clouds in the visual field), there is no easy fix to the problem.<p>
<li> To smooth out changes in wind settings, rather than producing a sudden gust, the wind is changed over about 1 second. In sharp wind gradients, this produces a series of ripples like turbulence. This is actually a realistic behaviour in this situation and hence left as it is. Some systems seem to take an unreasonable effort to reinit the environment (as must be done to set wind) - here the function call <i>setWindSmoothly()</i> in <i>local_weather.nas</i> should perhaps be replace by <i>setWind</i> to ease the load.
<li> The thermals in the soaring scenarios need GIT to work.<p>
</ul>
<h2><a name="appendix_A">Appendix A: An introduction to the algorithms</a></h2>
@ -340,6 +395,16 @@ To avoid unrealistically large changes in pressure when generating a transition
The weather algorithm is currently not able to handle the transition to tropical weather in the Hadley cell close to the equator, although tropical weather exists as a standalone tile and can be used in repetitive mode.<p>
<h3>The boundary layer computation</h3>
The boundary layer is only dynamically computed when 'terrain presampling' is active (otherwise the weather system has no information of the terrain and a schematic reduction of aloft winds by 2/3 is used instead). The assumption is that the boundary layer at median altitude has a thickness of 150 ft. Below the median altitude, the boundary layer is assumed to grow with 1/3 of the altitude difference, but to no more than 3000 ft while above it is assumed to shrink with 10% of the altitude difference, but to no more than 50 ft. <p>
The boundary layer effectiveness, i.e. the amount of windspeed reduction at the layer bottom is assumed to vary logarithmically with thickness - thicker layers are more effective, but not dramatically so. Over open water, the layer thickness is hence about 150 ft with a maximal reduction of 10%, in deep valleys the reduction factor can be as large as 70%. The interpolation of wind reduction inside the boundary layer is done linearly.<p>
Realistically, the boundary layer should also depend on terrain coverage. Due to the need for performance to sample terrain coverage in the vicinity of the aircraft frequently, this is currently not implemented.<p>
<h2>Credits</h2>
The model of a thermal has been developed by Patrice Poly. The shader code used to transform clouds is heavily based on prior work by Stuart Buchanan.<p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 25 KiB

BIN
Docs/menu3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

500
Nasal/compat_layer.nas Normal file
View file

@ -0,0 +1,500 @@
########################################################
# compatibility layer for local weather package
# Thorsten Renk, July 2010
########################################################
# function purpose
#
# setVisibility to set the visibility to a given value
# setRain to set rain to a given value
# setSnow to set snow to a given value
# setTurbulence to set turbulence to a given value
# setTemperature to set temperature to a given value
# setPressure to set pressure to a given value
# setDewpoint to set the dewpoint to a given value
# setWind to set wind
# setWindSmoothly to set the wind gradually across a second
# smooth_wind_loop helper function for setWindSmoothly
# create_cloud to place a single cloud into the scenery
# create_cloud_array to place clouds from storage arrays into the scenery
# move_cloud to move the cloud position
# remove_clouds to remove clouds by tile index
# waiting_loop to ensure tile removal calls do not overlap
# remove_tile_loop to remove a fixed number of clouds per frame
# get_elevation to get the terrain elevation at given coordinates
# get_elevation_vector to get terrain elevation at given coordinate vector
####################################
# set visibility to given value
####################################
var setVisibility = func (vis) {
# this is a rather dirty workaround till a better solution becomes available
# essentially we update all entries in config and reinit environment
var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
foreach (var e; entries_aloft) {
e.getNode("visibility-m",1).setValue(vis);
}
var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry");
foreach (var e; entries_boundary) {
e.getNode("visibility-m",1).setValue(vis);
}
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
####################################
# set rain to given value
####################################
var setRain = func (rain) {
# setting the lowest cloud layer to 30.000 ft is a workaround
# as rain is only created below that layer in default
setprop("environment/clouds/layer[0]/elevation-ft", 30000.0);
setprop("environment/metar/rain-norm",rain);
}
####################################
# set snow to given value
####################################
var setSnow = func (snow) {
# setting the lowest cloud layer to 30.000 ft is a workaround
# as snow is only created below that layer in default
setprop("environment/clouds/layer[0]/elevation-ft", 30000.0);
setprop("environment/metar/snow-norm",snow);
}
####################################
# set turbulence to given value
####################################
var setTurbulence = func (turbulence) {
# this is a rather dirty workaround till a better solution becomes available
# essentially we update all entries in config and reinit environment
var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
foreach (var e; entries_aloft) {
e.getNode("turbulence/magnitude-norm",1).setValue(turbulence);
}
# turbulence is slightly reduced in boundary layers
var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry");
var i = 1;
foreach (var e; entries_boundary) {
e.getNode("turbulence/magnitude-norm",1).setValue(turbulence * 0.25*i);
i = i + 1;
}
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
####################################
# set temperature to given value
####################################
var setTemperature = func (T) {
# this is a rather dirty workaround till a better solution becomes available
# essentially we update the entry in config and reinit environment
setprop(ec~"boundary/entry[0]/temperature-degc",T);
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
####################################
# set pressure to given value
####################################
var setPressure = func (p) {
# this is a rather dirty workaround till a better solution becomes available
# essentially we update the entry in config and reinit environment
setprop(ec~"boundary/entry[0]/pressure-sea-level-inhg",p);
setprop(ec~"aloft/entry[0]/pressure-sea-level-inhg",p);
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
####################################
# set dewpoint to given value
####################################
var setDewpoint = func (D) {
# this is a rather dirty workaround till a better solution becomes available
# essentially we update the entry in config and reinit environment
setprop(ec~"boundary/entry[0]/dewpoint-degc",D);
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
###########################################################
# set wind to given direction and speed
###########################################################
var setWind = func (dir, speed) {
# this is a rather dirty workaround till a better solution becomes available
# essentially we update all entries in config and reinit environment
var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
foreach (var e; entries_aloft) {
e.getNode("wind-from-heading-deg",1).setValue(dir);
e.getNode("wind-speed-kt",1).setValue(speed);
}
var entries_boundary = props.globals.getNode("environment/config/boundary", 1).getChildren("entry");
foreach (var e; entries_boundary) {
e.getNode("wind-from-heading-deg",1).setValue(dir);
e.getNode("wind-speed-kt",1).setValue(speed);
}
fgcommand("reinit", props.Node.new({subsystem:"environment"}));
}
###########################################################
# set wind smoothly to given direction and speed
# interpolating across several frames
###########################################################
var setWindSmoothly = func (dir, speed) {
var entries_aloft = props.globals.getNode("environment/config/aloft", 1).getChildren("entry");
var dir_old = entries_aloft[0].getNode("wind-from-heading-deg",1).getValue();
var speed_old = entries_aloft[0].getNode("wind-speed-kt",1).getValue();
var dir = dir * math.pi/180.0;
var dir_old = dir_old * math.pi/180.0;
var vx = speed * math.sin(dir);
var vx_old = speed_old * math.sin(dir_old);
var vy = speed * math.cos(dir);
var vy_old = speed_old * math.cos(dir_old);
smooth_wind_loop(vx,vy,vx_old, vy_old, 4, 4);
}
var smooth_wind_loop = func (vx, vy, vx_old, vy_old, counter, count_max) {
var time_delay = 0.9/count_max;
if (counter == 0) {return;}
var f = (counter -1)/count_max;
var vx_set = f * vx_old + (1-f) * vx;
var vy_set = f * vy_old + (1-f) * vy;
var speed_set = math.sqrt(vx_set * vx_set + vy_set * vy_set);
var dir_set = math.atan2(vx_set,vy_set) * 180.0/math.pi;
setWind(dir_set,speed_set);
settimer( func {smooth_wind_loop(vx,vy,vx_old,vy_old,counter-1, count_max); },time_delay);
}
###########################################################
# place a single cloud
###########################################################
var create_cloud = func(path, lat, long, alt, heading) {
var tile_counter = getprop(lw~"tiles/tile-counter");
var n = props.globals.getNode("local-weather/clouds", 1);
var c = n.getChild("tile",tile_counter,1);
var cloud_number = n.getNode("placement-index").getValue();
for (var i = cloud_number; 1; i += 1)
if (c.getChild("cloud", i, 0) == nil)
break;
cl = c.getChild("cloud", i, 1);
n.getNode("placement-index").setValue(i);
var model_number = n.getNode("model-placement-index").getValue();
var m = props.globals.getNode("models", 1);
for (var i = model_number; 1; i += 1)
if (m.getChild("model", i, 0) == nil)
break;
model = m.getChild("model", i, 1);
n.getNode("model-placement-index").setValue(i);
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("tile-index",1).setValue(tile_counter);
model.getNode("path", 1).setValue(path);
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("tile-index",1).setValue(tile_counter);
model.getNode("load", 1).remove();
n.getNode("cloud-number").setValue(n.getNode("cloud-number").getValue()+1);
# sort the model nodes into a vector
append(weather_tile_management.modelArrays[tile_counter-1],model);
# if weather dynamics is on, also create a timestamp property and sort into quadtree
if (getprop(lw~"config/dynamics-flag") == 1)
{
cl.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
var blat = getprop(lw~"tiles/tmp/latitude-deg");
var blon = getprop(lw~"tiles/tmp/longitude-deg");
var alpha = getprop(lw~"tmp/tile-orientation-deg");
#weather_dynamics.sort_into_quadtree(blat, blon, alpha, lat, long, weather_dynamics.cloudQuadtree, cl);
weather_dynamics.sort_into_quadtree(blat, blon, alpha, lat, long, weather_dynamics.cloudQuadtrees[tile_counter-1], cl);
}
}
###########################################################
# place a cloud layer from arrays, split across frames
###########################################################
var create_cloud_array = func (i, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation) {
if (getprop(lw~"tmp/thread-status") != "placing") {return;}
if (getprop(lw~"tmp/convective-status") != "idle") {return;}
if ((i < 0) or (i==0))
{
print("Cloud placement from array finished!");
setprop(lw~"tmp/thread-status", "idle");
return;
}
var k_max = 30;
var s = size(clouds_path);
if (s < k_max) {k_max = s;}
for (var k = 0; k < k_max; k = k+1)
{
#print(s, " ", k, " ", s-k-1, " ", clouds_path[s-k-1]);
#create_cloud(clouds_type[s-k-1], clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], 0.0, 0);
create_cloud(clouds_path[s-k-1], clouds_lat[s-k-1], clouds_lon[s-k-1], clouds_alt[s-k-1], clouds_orientation[s-k-1]);
}
setsize(clouds_path,s-k_max);
setsize(clouds_lat,s-k_max);
setsize(clouds_lon,s-k_max);
setsize(clouds_alt,s-k_max);
setsize(clouds_orientation,s-k_max);
settimer( func {create_cloud_array(i - k, clouds_path, clouds_lat, clouds_lon, clouds_alt, clouds_orientation ) }, 0 );
};
####################################################
# move a cloud
####################################################
var move_cloud = func (c, tile_index) {
# get the old spacetime position of the cloud
var lat_old = c.getNode("position/latitude-deg").getValue();
var lon_old = c.getNode("position/longitude-deg").getValue();
var alt = c.getNode("position/altitude-ft").getValue();
var timestamp = c.getNode("timestamp-sec").getValue();
# get windfield and time since last update
var windfield = weather_dynamics.get_windfield(tile_index);
var dt = weather_dynamics.time_lw - timestamp;
#print(dt * windfield[1]);
# update the spacetime position of the cloud
c.getNode("position/latitude-deg",1).setValue(lat_old + windfield[1] * dt * local_weather.m_to_lat);
c.getNode("position/longitude-deg",1).setValue(lon_old + windfield[0] * dt * local_weather.m_to_lon);
c.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
}
####################################################
# remove clouds by tile index
####################################################
var remove_clouds = func (index) {
var n = size(props.globals.getNode("local-weather/clouds").getChild("tile",index,1).getChildren("cloud"));
props.globals.getNode("local-weather/clouds", 1).removeChild("tile",index);
setprop(lw~"clouds/cloud-number",getprop(lw~"clouds/cloud-number")-n);
if (getprop(lw~"tmp/thread-flag") == 1)
{settimer( func {waiting_loop(index); },0);}
else
{
var modelNode = props.globals.getNode("models", 1).getChildren("model");
foreach (var m; modelNode)
{
if (m.getNode("tile-index",1).getValue() == index) {m.remove();}
}
}
}
# this is to avoid two tile removal loops starting at the same time
var waiting_loop = func (index) {
var status = getprop(lw~"tmp/thread-status");
if (status == "idle") {remove_tile_loop(index);}
else {
print("Removal of ",index, " waiting for idle thread...");
settimer( func {waiting_loop(index); },1.0);
}
}
var remove_tile_loop = func (index) {
var n = 100;
var flag_mod = 0;
var status = getprop(lw~"tmp/thread-status");
if ((status == "computing") or (status == "placing")) # the array is blocked
{
settimer( func {remove_tile_loop(index); },0); # try again next frame
return;
}
else if (status == "idle") # we initialize the loop
{
mvec = weather_tile_management.modelArrays[index-1];
msize = size(mvec);
setprop(lw~"tmp/last-reading-pos-mod", msize);
setprop(lw~"tmp/thread-status", "removing");
}
var lastpos = getprop(lw~"tmp/last-reading-pos-mod");
if (lastpos < (msize-1)) {var istart = lastpos;} else {var istart = (msize-1);}
if (istart<0) {istart=0;}
var i_min = istart - n;
if (i_min < -1) {i_min =-1;}
for (var i = istart; i > i_min; i = i- 1)
{
m = mvec[i];
m.remove();
}
if (i<0) {flag_mod = 1;}
if (flag_mod == 0) {setprop(lw~"tmp/last-reading-pos-mod",i); }
if (flag_mod == 0) # we still have work to do
{settimer( func {remove_tile_loop(index); },0);}
else
{
print("Tile deletion loop finished!");
setprop(lw~"tmp/thread-status", "idle");
setprop(lw~"clouds/placement-index",0);
setprop(lw~"clouds/model-placement-index",0);
setsize(weather_tile_management.modelArrays[index-1],0);
}
}
###########################################################
# get terrain elevation
###########################################################
var get_elevation = func (lat, lon) {
var info = geodinfo(lat, lon);
if (info != nil) {var elevation = info[0] * local_weather.m_to_ft;}
else {var elevation = -1.0;}
return elevation;
}
###########################################################
# get terrain elevation vector
###########################################################
var get_elevation_array = func (lat, lon) {
var elevation = [];
var n = size(lat);
for(var i = 0; i < n; i=i+1)
{
append(elevation, get_elevation(lat[i], lon[i]));
}
return elevation;
}
############################################################
# global variables
############################################################
# some common abbreviations
var lw = "/local-weather/";
var ec = "/environment/config/";
# storage arrays for model vector
var mvec = [];
var msize = 0;

File diff suppressed because it is too large Load diff

537
Nasal/weather_dynamics.nas Normal file
View file

@ -0,0 +1,537 @@
########################################################
# routines to simulate cloud wind drift and evolution
# Thorsten Renk, July 2010
########################################################
# function purpose
#
# get_windfield to get the current wind in the tile
# timing_loop to provide accurate timing information for wind drift calculations
# quadtree_loop to manage drift of clouds in the field of view
# weather_dynamics_loop to manage drift of weather effects, tile centers and interpolation points
# generate_quadtree_structure to generate a quadtree data structure used for managing the visual field
# sort_into_quadtree to sort objects into a quadtree structure
# quadtree_recursion to search the quadtree for objects in the visual field
# check_visibility to check if a quadrant is currently visible
# move_tile to move tile coordinates in the wind
# move_effect_volume to move an effect volume in the wind
# move_weather_station to move a weather station in the wind
# get_cartesian to get local Cartesian coordinates out of coordinates
####################################################
# get the windfield for a given locatio and altitude
# (currently constant, but supposed to be local later)
####################################################
var get_windfield = func (tile_index) {
var windfield = [];
if ((local_weather.wind_model_flag == 1) or (local_weather.wind_model_flag == 3))
{
var windspeed = tile_wind_speed[0] * kt_to_ms;
var wind_direction = tile_wind_direction[0];
}
else if ((local_weather.wind_model_flag ==2) or (local_weather.wind_model_flag == 4) or (local_weather.wind_model_flag == 5))
{
var windspeed = tile_wind_speed[tile_index-1] * kt_to_ms;
var wind_direction = tile_wind_direction[tile_index-1];
}
var windfield_x = -windspeed * math.sin(wind_direction * math.pi/180.0);
var windfield_y = -windspeed * math.cos(wind_direction * math.pi/180.0);
append(windfield,windfield_x);
append(windfield,windfield_y);
return windfield;
}
########################################################
# timing loop
# this gets the accurate time since the start of weather dynamics
# and hence the timestamps for cloud evolution since
# the available elapsed-time-sec is not accurate enough
########################################################
var timing_loop = func {
time_lw = time_lw + getprop("/sim/time/delta-sec");
if (getprop(lw~"timing-loop-flag") ==1) {settimer(timing_loop, 0);}
}
###########################################################
# quadtree loop
# the quadtree loop is a fast loop updating the position
# of visible objects in the field of view only
###########################################################
var quadtree_loop = func {
var vangle = 0.55 * getprop("/sim/current-view/field-of-view");
var viewdir = getprop("/sim/current-view/goal-heading-offset-deg");
var lat = getprop("position/latitude-deg");
var lon = getprop("position/longitude-deg");
var course = getprop("orientation/heading-deg");
cloud_counter = 0;
# pre-calculate trigonometry
tan_vangle = math.tan(vangle * math.pi/180.0);
# use the quadtree to move clouds inside the field of view
var tiles = props.globals.getNode(lw~"tiles").getChildren("tile");
foreach (t; tiles)
{
var generated_flag = t.getNode("generated-flag").getValue();
if (generated_flag == 1)
{
var index = t.getNode("tile-index").getValue();
current_tile_index_wd = index;
var blat = t.getNode("latitude-deg").getValue();
var blon = t.getNode("longitude-deg").getValue();
var alpha = t.getNode("orientation-deg").getValue();
var xy_vec = get_cartesian(blat, blon, alpha, lat, lon);
var beta = course - alpha - viewdir ;
cos_beta = math.cos(beta * math.pi/180.0);
sin_beta = math.sin(beta * math.pi/180.0);
plane_x = xy_vec[0]; plane_y = xy_vec[1];
quadtree_recursion(cloudQuadtrees[index-1],0,1,0.0,0.0);
}
}
# dynamically adjust the range of the processed view field
# if there are plenty of moving clouds nearby, no one pays attention to the small motion of distant clouds
# price to pay is that some clouds appear to jump once they get into range
if (cloud_counter < 150) {view_distance = view_distance * 1.1;}
else if (cloud_counter > 250) {view_distance = view_distance * 0.9;}
if (view_distance > 30000.0) {view_distance = 30000.0;}
#print(cloud_counter, " ", view_distance/1000.0);
# shift the tile centers with the windfield
var tiles = props.globals.getNode("local-weather/tiles", 1).getChildren("tile");
foreach (t; tiles) {move_tile(t);}
# loop over
if (getprop(lw~"dynamics-loop-flag") ==1) {settimer(quadtree_loop, 0);}
}
###########################################################
# weather_dynamics_loop
# the weather dynamics loop is a slow loop updating
# position and state of invisible objects, currently
# effect volumes and weather stations
###########################################################
var weather_dynamics_loop = func (index) {
var n = 20;
var i_max = index + n;
if (i_max > local_weather.n_effectVolumeArray) {i_max = local_weather.n_effectVolumeArray;}
for (var i = index; i < i_max; i = i+1)
{
move_effect_volume(local_weather.effectVolumeArray[i]);
}
index = index + n;
if (i >= local_weather.n_effectVolumeArray) {index = 0;}
var stations = props.globals.getNode(lw~"interpolation").getChildren("station");
foreach (s; stations)
{
move_weather_station(s);
}
if (getprop(lw~"dynamics-loop-flag") ==1) {settimer( func {weather_dynamics_loop(index); },0);}
}
###########################################################
# generate quadtree structure
###########################################################
var generate_quadtree_structure = func (depth, tree_base_vec) {
var c_vec = [];
for (var i=0; i<4; i=i+1)
{
if (depth == quadtree_depth)
{var c = [];}
else
{var c = generate_quadtree_structure(depth+1, tree_base_vec);}
if (depth==0)
{append(tree_base_vec,c); }
else
{append(c_vec,c); }
}
if (depth ==0) {return tree_base_vec;} else {return c_vec;}
}
###########################################################
# sort into quadtree
###########################################################
var sort_into_quadtree = func (blat, blon, alpha, lat, lon, tree, object) {
xy_vec = get_cartesian (blat, blon, alpha, lat, lon);
sorting_recursion (xy_vec[0], xy_vec[1], tree, object, 0);
}
var sorting_recursion = func (x, y, tree, object, depth) {
if (depth == quadtree_depth+1) {append(tree,object); return;}
var length_scale = 20000.0 / math.pow(2,depth);
# print("depth: ", depth, "x: ", x, "y: ",y);
if (y > 0.0)
{
if (x < 0.0)
{var v = tree[0]; x = x + 0.5 * length_scale; y = y - 0.5 * length_scale;}
else
{var v = tree[1]; x = x - 0.5 * length_scale; y = y - 0.5 * length_scale;}
}
else
{
if (x < 0.0)
{var v = tree[2]; x = x + 0.5 * length_scale; y = y + 0.5 * length_scale;}
else
{var v = tree[3]; x = x - 0.5 * length_scale; y = y + 0.5 * length_scale;}
}
sorting_recursion(x, y, v, object, depth+1);
}
####################################################
# quadtree recursive search
####################################################
var quadtree_recursion = func (tree, depth, flag, qx, qy) {
# flag = 0: quadrant invisible, stop search
# flag = 1: quadrant partially visible, continue search with visibility tests
# flag = 2: quadrant fully visible, no further visibility test needed
if (depth == quadtree_depth +1)
{
foreach (var c; tree)
{
compat_layer.move_cloud(c, current_tile_index_wd);
cloud_counter = cloud_counter + 1;
}
return;
}
for (var i =0; i<4; i=i+1)
{
if (flag==2) {quadtree_recursion(tree[i], depth+1, flag, qx, qy);}
else if (flag==1)
{
# compute the subquadrant coordinates
var length_scale = 20000.0 / math.pow(2,depth);
if (i==0) {var qxnew = qx - 0.5 * length_scale; var qynew = qy + 0.5 * length_scale;}
else if (i==1) {var qxnew = qx + 0.5 * length_scale; var qynew = qy + 0.5 * length_scale;}
else if (i==2) {var qxnew = qx - 0.5 * length_scale; var qynew = qy - 0.5 * length_scale;}
else if (i==3) {var qxnew = qx + 0.5 * length_scale; var qynew = qy - 0.5 * length_scale;}
var newflag = check_visibility(qxnew,qynew, length_scale);
if (newflag!=0) {quadtree_recursion(tree[i], depth+1, newflag, qxnew, qynew);}
}
}
}
####################################################
# quadrant visibility test
####################################################
var check_visibility = func (qx,qy, length_scale) {
# (qx,qy) are the quadrant coordinates in tile local Cartesian
# beta is the plane course in the tile local Cartesian
# the function returns a flag: 0: invisible 1: partially visible, track further 2: fully visible
# first translate/rotate (qx,qy) into the plane system
qx = qx - plane_x; qy = qy - plane_y;
var x = qx * cos_beta - qy * sin_beta;
var y = qy * cos_beta + qx * sin_beta;
# now get the maximum and minimum quadrant extensions
var ang_factor = abs(cos_beta) + abs(sin_beta); # a square seen from an angle extends larger
var xmax = x + 0.5 * length_scale * ang_factor;
var xmin = x - 0.5 * length_scale * ang_factor;
var ymax = y + 0.5 * length_scale * ang_factor;
var ymin = y - 0.5 * length_scale * ang_factor;
# now do visibility checks
if ((ymax < 0.0) and (ymin < 0.0)) # quadrant is behind us, we can never see it
{return 0;}
if (ymin > view_distance) # the quadrant is beyond visible range
{return 0;}
var xcomp_min = ymin * tan_vangle;
var xcomp_max = ymax * tan_vangle;
if ((ymax > 0.0) and (ymin < 0.0)) # object is at most partially visible, check if in visual cone at ymax
{
if ((xmax < -xcomp_max) and (xmin < -xcomp_max)) {return 0;}
if ((xmax > xcomp_max) and (xmin > xcomp_max)) {return 0;}
return 1;
}
# now we know the quadrant must be in front
# check if invisible
if ((xmax < -xcomp_max) and (xmin < -xcomp_max)) {return 0;}
if ((xmax > xcomp_max) and (xmin > xcomp_max)) {return 0;}
# check if completely visible
if ((xmax > -xcomp_min) and (xmin > -xcomp_min) and (xmax < xcomp_min) and (xmin < xcomp_min))
{return 2;}
# at this point, it must be partially visible
return 1;
}
####################################################
# move a tile
####################################################
var move_tile = func (t) {
# get the old spacetime position of the tile
var lat_old = t.getNode("latitude-deg").getValue();
var lon_old = t.getNode("longitude-deg").getValue();
var timestamp = t.getNode("timestamp-sec").getValue();
var tile_index = t.getNode("tile-index").getValue();
# if the tile is not yet generated, we use the windfield of the tile we're in
if (tile_index == -1)
{
tile_index = props.globals.getNode(lw~"tiles").getChild("tile",4).getNode("tile-index").getValue();
}
# get windfield and time since last update
var windfield = get_windfield(tile_index);
var dt = time_lw - timestamp;
# update the spacetime position of the tile
t.getNode("latitude-deg",1).setValue(lat_old + windfield[1] * dt * local_weather.m_to_lat);
t.getNode("longitude-deg",1).setValue(lon_old + windfield[0] * dt * local_weather.m_to_lon);
t.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
}
####################################################
# move an effect volume
####################################################
var move_effect_volume = func (e) {
# get the old spacetime position of the effect
var lat_old = e.getNode("position/latitude-deg").getValue();
var lon_old = e.getNode("position/longitude-deg").getValue();
var tile_index = e.getNode("tile-index").getValue();
var timestamp = e.getNode("timestamp-sec").getValue();
# get windfield and time since last update
var windfield = weather_dynamics.get_windfield(tile_index);
var dt = weather_dynamics.time_lw - timestamp;
# update the spacetime position of the effect
e.getNode("position/latitude-deg",1).setValue(lat_old + windfield[1] * dt * local_weather.m_to_lat);
e.getNode("position/longitude-deg",1).setValue(lon_old + windfield[0] * dt * local_weather.m_to_lon);
e.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
}
####################################################
# move a weather station
####################################################
var move_weather_station = func (s) {
# get the old spacetime position of the station
var lat_old = s.getNode("latitude-deg").getValue();
var lon_old = s.getNode("longitude-deg").getValue();
var tile_index = s.getNode("tile-index").getValue();
var timestamp = s.getNode("timestamp-sec").getValue();
# get windfield and time since last update
var windfield = weather_dynamics.get_windfield(tile_index);
var dt = weather_dynamics.time_lw - timestamp;
# update the spacetime position of the effect
s.getNode("latitude-deg",1).setValue(lat_old + windfield[1] * dt * local_weather.m_to_lat);
s.getNode("longitude-deg",1).setValue(lon_old + windfield[0] * dt * local_weather.m_to_lon);
s.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
}
###########################################################
# get local Cartesian coordinates
###########################################################
var get_cartesian = func (blat, blon, alpha, lat, lon) {
var xy_vec = [];
var phi = alpha * math.pi/180.0;
var delta_lat = lat - blat;
var delta_lon = lon - blon;
var x1 = delta_lon * lon_to_m;
var y1 = delta_lat * lat_to_m;
var x = x1 * math.cos(phi) - y1 * math.sin(phi);
var y = y1 * math.cos(phi) + x1 * math.sin(phi);
append(xy_vec,x);
append(xy_vec,y);
return xy_vec;
}
################################
# globals, constants, properties
################################
var lat_to_m = 110952.0; # latitude degrees to meters
var m_to_lat = 9.01290648208234e-06; # meters to latitude degrees
var ft_to_m = 0.30480;
var m_to_ft = 1.0/ft_to_m;
var inhg_to_hp = 33.76389;
var hp_to_inhg = 1.0/inhg_to_hp;
var kt_to_ms = 0.514;
var ms_to_kt = 1./kt_to_ms;
var lon_to_m = 0.0; # needs to be calculated dynamically
var m_to_lon = 0.0; # we do this on startup
# abbreviations
var lw = "/local-weather/";
# globals
var time_lw = 0.0;
# the quadtree structure
var cloudQuadtrees = [];
var quadtree_depth = 3;
# the wind info for the individual weather tiles
# (used for 'constant in tile' wind model)
var tile_wind_direction = [];
var tile_wind_speed = [];
# define these as global, as we need to evaluate them only once per frame
# but use them over and over
var tan_vangle = 0;
var cos_beta = 0;
var sin_beta = 0;
var plane_x = 0;
var plane_y = 0;
var current_tile_index_wd = 0;
var cloud_counter = 0;
var view_distance = 30000.0;
# create the loop flags
setprop(lw~"timing-loop-flag",0);
setprop(lw~"dynamics-loop-flag",0);

View file

@ -1,8 +1,21 @@
########################################################
# routines to set up, transform and manage weather tiles
# Thorsten Renk, April 2010
# Thorsten Renk, July 2010
########################################################
# function purpose
#
# tile_management_loop to decide if a tile is created, removed or considered current
# generate_tile to decide on orientation and type and set up all information for tile creation
# remove_tile to delete a tile by index
# change_active_tile to change the tile the aircraft is currently in and to generate neighbour info
# copy_entry to copy tile information from one node to another
# create_neighbour to set up information for a new neighbouring tile
# create_neighbours to initialize the 8 neighbours of the initial tile
# calc_geo to get local Cartesian geometry for latitude conversion
# get_lat to get latitude from Cartesian coordinates
# get_lon to get longitude from Cartesian coordinates
###################################
# tile management loop
@ -20,6 +33,7 @@ var distance_to_load = getprop(lw~"config/distance-to-load-tile-m");
var distance_to_remove = getprop(lw~"config/distance-to-remove-tile-m");
var current_heading = getprop("orientation/heading-deg");
var loading_flag = getprop(lw~"tmp/asymmetric-tile-loading-flag");
var this_frame_generated_flag = 0; # use this flag to avoid overlapping tile generation calls
foreach (var t; tNode) {
@ -41,15 +55,28 @@ foreach (var t; tNode) {
}
}
#print(d);
if ((d < distance_to_load) and (flag==0)) # the tile needs to be generated, unless it already has been
# the tile needs to be generated, unless it already has been
# and if no other tile has been generated in this loop cycle
# and the thread and convective system are idle
# (we want to avoid overlapping tile generation)
if ((d < distance_to_load) and (flag==0) and (this_frame_generated_flag == 0) and (getprop(lw~"tmp/thread-status") == "idle") and (getprop(lw~"tmp/convective-status") == "idle"))
{
setprop(lw~"tiles/tile-counter",getprop(lw~"tiles/tile-counter")+1);
print("Building tile unique index ",getprop(lw~"tiles/tile-counter"));
print("Building tile unique index ",getprop(lw~"tiles/tile-counter"), " in direction ",i);
generate_tile(code, tpos.lat(), tpos.lon(),i);
if (getprop(lw~"config/dynamics-flag") == 1)
{
var quadtree = [];
weather_dynamics.generate_quadtree_structure(0, quadtree);
append(weather_dynamics.cloudQuadtrees,quadtree);
}
t.getNode("generated-flag").setValue(1);
t.getNode("code",1).setValue(getprop(lw~"tiles/code"));
t.getNode("timestamp-sec").setValue(weather_dynamics.time_lw);
t.getNode("tile-index",1).setValue(getprop(lw~"tiles/tile-counter"));
this_frame_generated_flag = 1;
}
if ((d > distance_to_remove) and (flag==1)) # the tile needs to be deleted if it exists
@ -82,29 +109,82 @@ if (getprop(lw~"tile-loop-flag") ==1) {settimer(tile_management_loop, 5.0);}
var generate_tile = func (code, lat, lon, dir_index) {
# make sure the last tile call has finished, otherwise put on hold
#if ((getprop(lw~"tmp/thread-status") != "idle") or (getprop(lw~"tmp/convective-status") != "idle"))
# {
# print("Tile generation overlap, delaying...");
# settimer( func {generate_tile(code, lat, lon, dir_index);}, 1);
# }
setprop(lw~"tiles/tmp/latitude-deg", lat);
setprop(lw~"tiles/tmp/longitude-deg",lon);
setprop(lw~"tiles/tmp/code",code);
# do windspeed and orientation before presampling check, but test not to do it again
if (((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling-status") == "idle")) or (getprop(lw~"tmp/presampling-flag") == 0))
{
var alpha = getprop(lw~"tmp/tile-orientation-deg");
if ((local_weather.wind_model_flag == 2) or (local_weather.wind_model_flag ==4))
{
alpha = alpha + 2.0 * (rand()-0.5) * 10.0;
# account for the systematic spin of weather systems around a low pressure
# core dependent on hemisphere
if (lat >0.0) {alpha = alpha -3.0;}
else {alpha = alpha +3.0;}
setprop(lw~"tmp/tile-orientation-deg",alpha);
# compute the new windspeed
var windspeed = getprop(lw~"tmp/windspeed-kt");
windspeed = windspeed + 2.0 * (rand()-0.5) * 2.0;
if (windspeed < 0) {windspeed = rand();}
setprop(lw~"tmp/windspeed-kt");
# store the tile orientation and wind strength in an array for fast processing
append(weather_dynamics.tile_wind_direction, alpha);
append(weather_dynamics.tile_wind_speed, windspeed);
}
else if (local_weather.wind_model_flag ==5) # alpha and windspeed are calculated
{
var res = local_weather.wind_interpolation(lat,lon,0.0);
alpha = res[0];
setprop(lw~"tmp/tile-orientation-deg",alpha);
var windspeed = res[1];
setprop(lw~"tmp/windspeed-kt",windspeed);
append(weather_dynamics.tile_wind_direction,res[0]);
append(weather_dynamics.tile_wind_speed,res[1]);
}
props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("orientation-deg").setValue(alpha);
}
# now see if we need to presample the terrain
if ((getprop(lw~"tmp/presampling-flag") == 1) and (getprop(lw~"tmp/presampling-status") == "idle"))
{
local_weather.terrain_presampling_start(lat, lon, 1000, 40000, getprop(lw~"tmp/tile-orientation-deg"));
setprop(lw~"tiles/tmp/dir-index",dir_index);
return;
}
# now allow for some change in tile orientation
var alpha = getprop(lw~"tmp/tile-orientation-deg");
alpha = alpha + 2.0 * (rand()-0.5) * 10.0;
# account for the systematic spin of weather systems around a low pressure core dependent on hemisphere
if (lat >0.0) {alpha = alpha -3.0;}
else {alpha = alpha +3.0;}
setprop(lw~"tmp/tile-orientation-deg",alpha);
print("Current tile type: ", code);
if (getprop(lw~"tmp/tile-management") == "repeat tile")
{
@ -124,6 +204,7 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile")
else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();}
else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();}
else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();}
else {print("Repeat tile not implemented with this tile type!");}
}
else if (getprop(lw~"tmp/tile-management") == "realistic weather")
{
@ -233,6 +314,19 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warmfront3_tile();}
}
else if (code == "coldfront")
{
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_cold_sector_tile();}
else if ((dir_index ==3) or (dir_index ==5))
{weather_tiles.set_coldfront_tile();}
else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warm_sector_tile();}
}
else
{
print("Realistic weather not implemented with this tile type!");
}
} # end if mode == realistic weather
}
@ -242,128 +336,42 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
# tile removal call
###################################
var remove_tile_loop = func (index) {
var n = 100;
var flag_mod = 0;
var status = getprop(lw~"tmp/thread-status");
if ((status == "computing") or (status == "placing")) # the array is blocked
{
settimer( func {remove_tile_loop(index); },0); # try again next frame
return;
}
else if (status == "idle") # we initialize the loop
{
mvec = props.globals.getNode("models", 1).getChildren("model");
msize = size(mvec);
setprop(lw~"tmp/last-reading-pos-mod", msize);
setprop(lw~"tmp/thread-status", "removing");
}
var lastpos = getprop(lw~"tmp/last-reading-pos-mod");
# print("msize: ", msize, "lastpos ", lastpos);
if (lastpos < (msize-1)) {var istart = lastpos;} else {var istart = (msize-1);}
if (istart<0) {istart=0;}
var i_min = istart - n;
if (i_min < -1) {i_min =-1;}
for (var i = istart; i > i_min; i = i- 1)
{
m = mvec[i];
if (m.getNode("legend",1).getValue() == "Cloud")
{
if (m.getNode("tile-index").getValue() == index)
{
m.remove();
}
}
}
if (i<0) {flag_mod = 1;}
if (flag_mod == 0) {setprop(lw~"tmp/last-reading-pos-mod",i); }
if (flag_mod == 0) # we still have work to do
{settimer( func {remove_tile_loop(index); },0);}
else
{
print("Tile deletion loop finished!");
setprop(lw~"tmp/thread-status", "idle");
setprop(lw~"clouds/placement-index",0);
setprop(lw~"clouds/model-placement-index",0);
}
}
# this is to avoid two tile removal loops starting at the same time
var waiting_loop = func (index) {
var status = getprop(lw~"tmp/thread-status");
if (status == "idle") {remove_tile_loop(index);}
else {
print("Removal of ",index, " waiting for idle thread...");
settimer( func {waiting_loop(index); },1.0);
}
}
var remove_tile = func (index) {
var n = size(props.globals.getNode("local-weather/clouds").getChild("tile",index,1).getChildren("cloud"));
props.globals.getNode("local-weather/clouds", 1).removeChild("tile",index);
setprop(lw~"clouds/cloud-number",getprop(lw~"clouds/cloud-number")-n);
if (getprop(lw~"tmp/thread-flag") == 1)
#{remove_tile_loop(index);}
#{waiting_loop(index);}
# call the model removal in the next frame, because its initialization takes time
# and we do a lot in this frame
{settimer( func {waiting_loop(index); },0);}
else
{
var modelNode = props.globals.getNode("models", 1).getChildren("model");
foreach (var m; modelNode)
{
if (m.getNode("legend").getValue() == "Cloud")
{
if (m.getNode("tile-index").getValue() == index)
{
m.remove();
}
}
}
}
compat_layer.remove_clouds(index);
var effectNode = props.globals.getNode("local-weather/effect-volumes").getChildren("effect-volume");
var ecount = 0;
foreach (var e; effectNode)
{
if (e.getNode("tile-index").getValue() == index)
{
e.remove();
setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")-1);
ecount = ecount + 1;
}
}
setprop(lw~"effect-volumes/number",getprop(lw~"effect-volumes/number")- ecount);
# set placement indices to zero to reinitiate search for free positions
setprop(lw~"clouds/placement-index",0);
setprop(lw~"clouds/model-placement-index",0);
setprop(lw~"effect-volumes/effect-placement-index",0);
# remove quadtree structures
if (getprop(lw~"config/dynamics-flag") ==1)
{
setsize(weather_dynamics.cloudQuadtrees[index-1],0);
}
# rebuild effect volume vector
local_weather.assemble_effect_array();
}
@ -498,6 +506,8 @@ t.getNode("longitude-deg").setValue(f.getNode("longitude-deg").getValue());
t.getNode("generated-flag").setValue(f.getNode("generated-flag").getValue());
t.getNode("tile-index").setValue(f.getNode("tile-index").getValue());
t.getNode("code").setValue(f.getNode("code").getValue());
t.getNode("timestamp-sec").setValue(f.getNode("timestamp-sec").getValue());
t.getNode("orientation-deg").setValue(f.getNode("orientation-deg").getValue());
}
#####################################
@ -525,6 +535,8 @@ t.getNode("longitude-deg",1).setValue(blon + get_lon(x,y,phi));
t.getNode("generated-flag",1).setValue(0);
t.getNode("tile-index",1).setValue(-1);
t.getNode("code",1).setValue("");
t.getNode("timestamp-sec",1).setValue(weather_dynamics.time_lw);
t.getNode("orientation-deg",1).setValue(0.0);
}
#####################################
@ -546,6 +558,8 @@ setprop(lw~"tiles/tile[0]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[0]/generated-flag",0);
setprop(lw~"tiles/tile[0]/tile-index",-1);
setprop(lw~"tiles/tile[0]/code","");
setprop(lw~"tiles/tile[0]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[0]/orientation-deg",0.0);
x = 0.0; y = 40000.0;
setprop(lw~"tiles/tile[1]/latitude-deg",blat + get_lat(x,y,phi));
@ -553,6 +567,8 @@ setprop(lw~"tiles/tile[1]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[1]/generated-flag",0);
setprop(lw~"tiles/tile[1]/tile-index",-1);
setprop(lw~"tiles/tile[1]/code","");
setprop(lw~"tiles/tile[1]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[1]/orientation-deg",0.0);
x = 40000.0; y = 40000.0;
setprop(lw~"tiles/tile[2]/latitude-deg",blat + get_lat(x,y,phi));
@ -560,6 +576,8 @@ setprop(lw~"tiles/tile[2]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[2]/generated-flag",0);
setprop(lw~"tiles/tile[2]/tile-index",-1);
setprop(lw~"tiles/tile[2]/code","");
setprop(lw~"tiles/tile[2]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[2]/orientation-deg",0.0);
x = -40000.0; y = 0.0;
setprop(lw~"tiles/tile[3]/latitude-deg",blat + get_lat(x,y,phi));
@ -567,6 +585,8 @@ setprop(lw~"tiles/tile[3]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[3]/generated-flag",0);
setprop(lw~"tiles/tile[3]/tile-index",-1);
setprop(lw~"tiles/tile[3]/code","");
setprop(lw~"tiles/tile[3]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[3]/orientation-deg",0.0);
# this is the current tile
x = 0.0; y = 0.0;
@ -575,6 +595,8 @@ setprop(lw~"tiles/tile[4]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[4]/generated-flag",1);
setprop(lw~"tiles/tile[4]/tile-index",1);
setprop(lw~"tiles/tile[4]/code","");
setprop(lw~"tiles/tile[4]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[4]/orientation-deg",getprop(lw~"tmp/tile-orientation-deg"));
x = 40000.0; y = 0.0;
@ -583,6 +605,8 @@ setprop(lw~"tiles/tile[5]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[5]/generated-flag",0);
setprop(lw~"tiles/tile[5]/tile-index",-1);
setprop(lw~"tiles/tile[5]/code","");
setprop(lw~"tiles/tile[5]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[5]/orientation-deg",0.0);
x = -40000.0; y = -40000.0;
setprop(lw~"tiles/tile[6]/latitude-deg",blat + get_lat(x,y,phi));
@ -590,6 +614,8 @@ setprop(lw~"tiles/tile[6]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[6]/generated-flag",0);
setprop(lw~"tiles/tile[6]/tile-index",-1);
setprop(lw~"tiles/tile[6]/code","");
setprop(lw~"tiles/tile[6]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[6]/orientation-deg",0.0);
x = 0.0; y = -40000.0;
setprop(lw~"tiles/tile[7]/latitude-deg",blat + get_lat(x,y,phi));
@ -597,6 +623,8 @@ setprop(lw~"tiles/tile[7]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[7]/generated-flag",0);
setprop(lw~"tiles/tile[7]/tile-index",-1);
setprop(lw~"tiles/tile[7]/code","");
setprop(lw~"tiles/tile[7]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[7]/orientation-deg",0.0);
x = 40000.0; y = -40000.0;
setprop(lw~"tiles/tile[8]/latitude-deg",blat + get_lat(x,y,phi));
@ -604,6 +632,8 @@ setprop(lw~"tiles/tile[8]/longitude-deg",blon + get_lon(x,y,phi));
setprop(lw~"tiles/tile[8]/generated-flag",0);
setprop(lw~"tiles/tile[8]/tile-index",-1);
setprop(lw~"tiles/tile[8]/code","");
setprop(lw~"tiles/tile[8]/timestamp-sec",weather_dynamics.time_lw);
setprop(lw~"tiles/tile[8]/orientation-deg",0.0);
}
###################
@ -622,10 +652,9 @@ var lon_to_m = 0.0; #local_weather.lon_to_m;
var m_to_lon = 0.0; # local_weather.m_to_lon;
var lw = "/local-weather/";
# storage array for model vector
var mvec = [];
var msize = 0;
var modelArrays = [];
###################
# helper functions

View file

@ -1,3 +1,20 @@
########################################################
# routines to set up weather tiles
# Thorsten Renk, July 2010
########################################################
# function purpose
#
# tile_start to execute jobs common for all tiles on startup
# tile_finished to execute jobs common for all tiles when tile creation is done
# set_NN_tile to set a weather tile of type NN
# create_NN to create the cloud configuration NN
# adjust_p to make sure pressure variation cannot exceed limits between tiles
# calc_geo to get local Cartesian geometry for latitude conversion
# get_lat to get latitude from Cartesian coordinates
# get_lon to get longitude from Cartesian coordinates
####################################
# tile setup calls
####################################
@ -6,14 +23,30 @@ var tile_start = func {
if (getprop(lw~"tmp/thread-flag") == 1){setprop(lw~"tmp/thread-status","computing");}
# generate a handling array for models
var array = [];
append(weather_tile_management.modelArrays,array);
}
var tile_finished = func {
var current_code = getprop(lw~"tiles/code");
setprop(lw~"clouds/placement-index",0);
setsize(elat,0); setsize(elon,0); setsize(erad,0);
var dir_index = getprop(lw~"tiles/tmp/dir-index");
props.globals.getNode(lw~"tiles").getChild("tile",dir_index).getNode("code").setValue(current_code);
local_weather.assemble_effect_array();
print("Finished setting up tile type ",current_code, " in direction ",dir_index);
if (getprop(lw~"tmp/thread-flag") == 1){setprop(lw~"tmp/thread-status","placing");}
else {local_weather.assemble_effect_array();} # otherwise this is done at the end of the placement loop
}
@ -28,6 +61,8 @@ var set_4_8_stratus_tile = func {
tile_start();
setprop(lw~"tiles/code","test");
var x = 0.0;
var y = 0.0;
var lat = 0.0;
@ -48,28 +83,10 @@ calc_geo(blat);
local_weather.set_weather_station(blat, blon, 20000.0, 14.0, 12.0, 29.78);
create_2_8_cirrocumulus(blat, blon, 12000.0, alpha);
#create_2_8_sstratus_streak(blat, blon,5000.0,0.0);
#x = 2.0 * (rand()-0.5) * 12000;
#y = 2.0 * (rand()-0.5) * 12000;
#create_medium_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), 3000.0+alt_offset, alpha);
create_4_8_cirrocumulus_bank(blat, blon, 6000.0, 0.0);
#x = 2.0 * (rand()-0.5) * 12000;
#y = 2.0 * (rand()-0.5) * 12000;
#create_medium_thunderstorm(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), 3000.0+alt_offset, alpha);
# create_big_thunderstorm(blat, blon, 3000.0+alt_offset, alpha);
#var strength = 1.0;
#var n = int(4000 * strength) * 0.2;
#local_weather.cumulus_exclusion_layer(blat, blon, 3000.0+alt_offset, n, 20000.0, 20000.0, alpha, 0.0, 2.5, size(elat), elat, elon, erad);
# some turbulence below the convection layer
#local_weather.create_effect_volume(3, blat, blon, 20000.0, 20000.0, alpha, 0.0, 3500.0+alt_offset, -1, -1, -1, 0.4, -1,0 );
tile_finished();
@ -186,7 +203,7 @@ if (rn > 0.5)
y = 2.0 * (rand()-0.5) * 18000;
alt = 25000.0 + rand() * 5000.0;
var path = local_weather.select_cloud_model("Cirrus", "small");
local_weather.create_cloud("Cirrus", path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha, 0);
compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha);
}
if (rand() > 2.0 * strength)
@ -195,7 +212,7 @@ if (rn > 0.5)
y = 2.0 * (rand()-0.5) * 18000;
alt = 25000.0 + rand() * 5000.0;
var path = local_weather.select_cloud_model("Cirrus", "small");
local_weather.create_cloud("Cirrus", path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha, 0);
compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset,alpha);
}
}
else if (rn > 0.0)
@ -265,8 +282,8 @@ if (rn > 0.833)
x = 2.0 * (rand()-0.5) * 5000;
y = 2.0 * (rand()-0.5) * 5000;
local_weather.create_streak("Altocumulus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 12000.0+alt+alt_offset,30,1000.0,0.2, 30,1000.0,0.2,alpha ,1.0);
local_weather.randomize_pos("Altocumulus",1500.0,800.0,800.0,alpha);
local_weather.create_streak("Altocumulus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 12000.0+alt+alt_offset,1500.0,30,1000.0,0.2,800.0,30,1000.0,0.2,800.0,alpha ,1.0);
}
else if (rn > 0.666)
{
@ -276,11 +293,11 @@ else if (rn > 0.666)
x = 2.0 * (rand()-0.5) * 10000;
y = 2.0 * (rand()-0.5) * 10000;
local_weather.create_streak("Altocumulus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 12000.0+alt+alt_offset,25,700.0,0.2, 10,700.0,0.2,alpha ,1.4);
local_weather.create_streak("Altocumulus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 12000.0+alt+alt_offset,1500.0,25,700.0,0.2,800.0,10,700.0,0.2,800.0,alpha ,1.4);
x = 2.0 * (rand()-0.5) * 10000;
y = 2.0 * (rand()-0.5) * 10000;
local_weather.create_streak("Altocumulus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 12000.0+alt+alt_offset,22,750.0,0.2, 8,750.0,0.2,alpha ,1.1);
local_weather.randomize_pos("Altocumulus",1500.0,800.0,800.0,alpha);
local_weather.create_streak("Altocumulus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 12000.0+alt+alt_offset,1500.0,22,750.0,0.2,800.0,8,750.0,0.2,800.0,alpha ,1.1);
}
else if (rn > 0.5)
{
@ -291,8 +308,8 @@ else if (rn > 0.5)
x = 2.0 * (rand()-0.5) * 3000;
y = 2.0 * (rand()-0.5) * 3000;
local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 22000.0+alt+alt_offset,3,9000.0,0.0, 1,8000.0,0.0,alpha ,1.0);
local_weather.randomize_pos("Cirrus",1500.0,800.0,800.0,alpha);
local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 22000.0+alt+alt_offset,1500.0,3,9000.0,0.0, 800.0, 1,8000.0,0.0,800,0,alpha ,1.0);
}
else if (rn > 0.333)
{
@ -318,8 +335,8 @@ else if (rn > 0.166)
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"];
local_weather.create_streak("Stratus (structured)",blat, blon, alt+6000.0+alt_offset+size_offset,18,0.0,0.3,18,0.0,0.3,0.0,1.0);
local_weather.randomize_pos("Stratus (structured)",1000.0,20000.0,20000.0,0.0);
local_weather.create_streak("Stratus (structured)",blat, blon, alt+6000.0+alt_offset+size_offset,1000.0,18,0.0,0.3,20000.0,18,0.0,0.3,20000.0,0.0,1.0);
}
else if (rn > 0.0)
{
@ -333,7 +350,7 @@ else if (rn > 0.0)
var path = local_weather.select_cloud_model("Cirrocumulus", "large");
local_weather.create_cloud("Cirrocumulus", path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +22000,alpha, 0);
compat_layer.create_cloud(path, blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), alt + alt_offset +22000,alpha);
}
@ -691,7 +708,7 @@ calc_geo(blat);
# get probabilistic values for the weather parameters
var vis = 15000.0 + rand() * 10000.0;
var vis = 10000.0 + rand() * 8000.0;
var T = 16.0 + rand() * 10.0;
var spread = 2.0 + 2.0 * rand();
var D = T - spread;
@ -706,13 +723,59 @@ var alt = spread * 1000.0;
var rn = rand();
if (rn > 0.0)
if (rn > 0.8)
{
# cloud scenario 1: weak Cumulus development, some Cirrostratus
var strength = 0.3 + rand() * 0.2;
local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
create_4_8_cirrostratus_patches(blat, blon, alt+alt_offset+25000.0, alpha);
}
else if (rn > 0.6)
{
# cloud scenario 2: weak Cumulus development under Altostratus streaks
var strength = 0.1 + rand() * 0.1;
local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"];
create_2_8_sstratus_streak(blat, blon, alt+alt_offset + size_offset + 2000.0, alpha);
create_2_8_sstratus_streak(blat, blon, alt+alt_offset + size_offset + 4000.0, alpha);
}
else if (rn > 0.4)
{
# cloud scenario 3: Cirrocumulus bank
var strength = 0.05 + rand() * 0.05;
local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Cirrocumulus"];
create_4_8_cirrocumulus_bank(blat, blon, alt+alt_offset + size_offset + 7000.0, alpha);
}
else if (rn > 0.2)
{
# cloud scenario 4: Cirrocumulus undulatus
var strength = 0.05 + rand() * 0.05;
local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Cirrocumulus"];
create_4_8_cirrocumulus_undulatus(blat, blon, alt+alt_offset + size_offset + 6000.0, alpha);
}
else if (rn > 0.0)
{
# cloud scenario 5: weak Cumulus development under scattered Altostratus
var strength = 0.15 + rand() * 0.15;
local_weather.create_cumosys(blat,blon, alt + alt_offset, get_n(strength), 20000.0);
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"];
local_weather.create_streak("Stratus (structured)",blat, blon, alt+4000.0+alt_offset+size_offset,1000.0,14,0.0,0.3,20000.0,14,0.0,0.3,20000.0,0.0,1.0);
}
tile_finished();
@ -810,7 +873,7 @@ if (rn > 0.2)
}
else if (rn > 0.0)
{
# cloud scenario 1: Single big storm
# cloud scenario 2: Single big storm
x = 2.0 * (rand()-0.5) * 12000;
y = 2.0 * (rand()-0.5) * 12000;
@ -935,13 +998,13 @@ local_weather.cumulus_exclusion_layer(blat+get_lat(x,y,phi), blon+ get_lon(x,y,p
x = 0.0;
y = 13000.0;
local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,20,2000.0,0.2,3,1500.0,0.2,alpha,1.0);
local_weather.randomize_pos("Stratus (thin)",0.0,1200.0,1200.0,0.0);
local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,0.0,20,2000.0,0.2,1200.0,3,1500.0,0.2,1200.0,alpha,1.0);
x = 0.0;
y = -3000.0;
local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,20,2000.0,0.2,3,1500.0,0.2,alpha,1.0);
local_weather.randomize_pos("Stratus (thin)",0.0,1200.0,1200.0,0.0);
local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,0.0,20,2000.0,0.2,1200.0,3,1500.0,0.2,1200.0,alpha,1.0);
# some turbulence in the convection layer
@ -1025,8 +1088,8 @@ x = 2.0 * (rand()-0.5) * 3000;
y = 2.0 * (rand()-0.5) * 3000 - 12000.0;
local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 25000.0+alt+alt_offset,3,11000.0,0.0, 1,8000.0,0.0,alpha ,1.0);
local_weather.randomize_pos("Cirrus",1500.0,800.0,10000.0,alpha);
local_weather.create_streak("Cirrus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 25000.0+alt+alt_offset,1500.0,3,11000.0,0.0, 800.0, 1,8000.0,0.0,10000.0,alpha ,1.0);
# followed by random patches of Cirrostratus
@ -1035,8 +1098,8 @@ for (var i=0; i<6; i=i+1)
var x = 2.0 * (rand()-0.5) * 15000;
var y = 2.0 * (rand()-0.5) * 10000 + 10000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset,4,2300.0,0.2,4,2300.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta);
local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 18000 + alt + alt_offset,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
}
tile_finished();
@ -1080,7 +1143,7 @@ calc_geo(blat);
var vis = 15000.0 + rand() * 5000.0;
var T = 13.0 + rand() * 8.0;
var spread = 2.0 + 2.0 * rand();
var spread = 2.5 + 2.5 * rand();
var D = T - spread;
var p = 1005 + rand() * 10.0; p = adjust_p(p);
@ -1113,8 +1176,8 @@ for (var i=0; i<3; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 5000 - 15000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset,4,2300.0,0.2,4,2300.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta);
local_weather.create_streak("Cirrostratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 15000 + alt + alt_offset,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
}
# patches of thin Altostratus
@ -1124,8 +1187,8 @@ for (var i=0; i<14; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 9000 - 10000.0;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +12000.0,4,950.0,0.2,6,950.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus (thin)",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus (thin)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +12000.0,300.0,4,950.0,0.2,500.0,6,950.0,0.2,500.0,alpha+beta,1.0);
}
# patches of structured Stratus
@ -1135,8 +1198,8 @@ for (var i=0; i<10; i=i+1)
var x = 2.0 * (rand()-0.5) * 9000;
var y = 2.0 * (rand()-0.5) * 9000 + 2000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (structured)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset+9000.0,5,900.0,0.2,7,900.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus (structured)",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus (structured)",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset+9000.0,300.0,5,900.0,0.2,500.0,7,900.0,0.2,500.0,alpha+beta,1.0);
}
@ -1145,8 +1208,8 @@ local_weather.randomize_pos("Stratus (structured)",300.0,500.0,500.0,alpha+beta)
var x = 0.0;
var y = 8000.0;
local_weather.create_streak("Stratus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +5000.0,30,0.0,0.2,10,0.0,0.2,alpha,1.0);
local_weather.randomize_pos("Stratus",1000.0,20000.0,12000.0,alpha);
local_weather.create_streak("Stratus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +5000.0,1000.0,30,0.0,0.2,20000.0,10,0.0,0.2,12000.0,alpha,1.0);
tile_finished();
@ -1189,7 +1252,7 @@ calc_geo(blat);
var vis = 12000.0 + rand() * 3000.0;
var T = 15.0 + rand() * 7.0;
var spread = 2.0 + 1.0 * rand();
var spread = 2.5 + 1.5 * rand();
var D = T - spread;
var p = 1005 + rand() * 10.0; p = adjust_p(p);
@ -1212,7 +1275,7 @@ x = -15000.0; y = -15000.0;
local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
var alt = spread * 1000.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft;
# closed Stratus layer
@ -1220,8 +1283,8 @@ var alt = spread * 1000.0;
var x = 0.0;
var y = -8000.0;
local_weather.create_streak("Stratus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +1000.0,32,1250.0,0.2,20,1250.0,0.2,alpha,1.0);
local_weather.randomize_pos("Stratus",500.0,400.0,400.0,alpha);
local_weather.create_streak("Stratus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset +1000.0,500.0,32,1250.0,0.2,400.0,20,1250.0,0.2,400.0,alpha,1.0);
@ -1231,8 +1294,8 @@ local_weather.randomize_pos("Stratus",500.0,400.0,400.0,alpha);
var x = 0.0;
var y = 8000.0;
local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,32,1250.0,0.0,20,1250.0,0.0,alpha,1.0);
local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha);
local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,500.0,32,1250.0,0.0,200.0,20,1250.0,0.0,200.0,alpha,1.0);
# some rain beneath the stratus
@ -1286,7 +1349,7 @@ calc_geo(blat);
var vis = 12000.0 + rand() * 3000.0;
var T = 17.0 + rand() * 6.0;
var spread = 1.0 + 1.0 * rand();
var spread = 2.0 + 1.0 * rand();
var D = T - spread;
var p = 1005 + rand() * 10.0; p = adjust_p(p);
@ -1309,15 +1372,15 @@ x = -15000.0; y = -15000.0;
local_weather.set_weather_station(blat +get_lat(x,y,phi), blon + get_lon(x,y,phi), vis, T, D, p * hp_to_inhg);
# altitude for the lowest layer
var alt = spread * 1000.0;
var alt = spread * 1000.0 + local_weather.cloud_vertical_size_map["Nimbus"] * 0.5 * m_to_ft;
# low Nimbostratus layer
var x = 0.0;
var y = -5000.0;
local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,32,1250.0,0.0,24,1250.0,0.0,alpha,1.0);
local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha);
local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,500.0,32,1250.0,0.0,200.0,24,1250.0,0.0,200.0,alpha,1.0);
# a little patchy structured Stratus above for effect
@ -1328,8 +1391,8 @@ create_2_8_sstratus(blat, blon, alt+alt_offset+3000.0, alpha);
var x = 0.0;
var y = 14000.0;
local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,25,1600.0,0.2,9,1400.0,0.3,alpha,1.0);
local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha);
local_weather.create_streak("Nimbus",blat +get_lat(x,y,phi), blon+get_lon(x,y,phi), alt+alt_offset,500.0,25,1600.0,0.2,200.0,9,1400.0,0.3,200.0,alpha,1.0);
# rain beneath the Nimbostratus
@ -1367,9 +1430,9 @@ local_weather.set_weather_station(blat, blon, 35000.0, 22.0, 14.0, 30.02);
# then draw the Altocumulus streaks, dense at 15.000 ft, sparse at 17.000 ft
local_weather.create_streak("Altocumulus",blat, blon, 15000.0+alt_offset,40,1000.0,0.2, 40,1000.0,0.2,alpha ,1.0);
local_weather.create_streak("Altocumulus",blat, blon, 17000.0+alt_offset,18,2000.0,0.35,18,2000.0,0.35,alpha,1.0);
local_weather.randomize_pos("Altocumulus",1500.0,800.0,800.0,alpha);
local_weather.create_streak("Altocumulus",blat, blon, 15000.0+alt_offset,1500.0,40,1000.0,0.2,800.0, 40,1000.0,0.2,800.0,alpha ,1.0);
local_weather.create_streak("Altocumulus",blat, blon, 17000.0+alt_offset,1500.0,18,2000.0,0.35,800.0,18,2000.0,0.35,800.0,alpha,1.0);
tile_finished();
@ -1409,12 +1472,12 @@ local_weather.set_weather_station(blat, blon, 10000.0, 14.0, 12.0, 29.78);
# then draw the Stratus layers
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus"];
local_weather.create_streak("Stratus",blat, blon, 1500.0+alt_offset+size_offset,32,1250.0,0.2, 32,1250.0,0.2,0.0 ,1.0);
local_weather.randomize_pos("Stratus",0.0,600.0,600.0,0.0);
local_weather.create_streak("Stratus",blat, blon, 1500.0+alt_offset+size_offset,0.0,32,1250.0,0.2,600.0, 32,1250.0,0.2,600.0,0.0 ,1.0);
size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"];
local_weather.create_streak("Stratus (structured)",blat, blon, 5000.0+alt_offset+size_offset,16,2500.0,0.3,16,2500.0,0.3,0.0,1.0);
local_weather.randomize_pos("Stratus (structured)",0.0,1200.0,1200.0,0.0);
local_weather.create_streak("Stratus (structured)",blat, blon, 5000.0+alt_offset+size_offset,0.0,16,2500.0,0.3,1200.0,16,2500.0,0.3,1200.0,0.0,1.0);
# reduce visibility even more below lowest layer
@ -1466,13 +1529,13 @@ local_weather.set_weather_station(blat+get_lat(x,y,phi), blon+get_lon(x,y,phi),
x = 0.0; y = -15000.0;
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus"];
local_weather.create_streak("Stratus",blat, blon, 3000.0+alt_offset+size_offset,17,2500.0,0.2, 6,2000.0,0.2,alpha ,1.0);
local_weather.randomize_pos("Stratus",500.0,1100.0,1100.0,alpha);
local_weather.create_streak("Stratus",blat, blon, 3000.0+alt_offset+size_offset,500.0,17,2500.0,0.2,1100.0, 6,2000.0,0.2,1100.0,alpha ,1.0);
x = 0.0; y = 0.0;
var size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus"];
local_weather.create_streak("Stratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 2000.0+alt_offset+size_offset,40,1000.0,0.2, 10,1000.0,0.2,alpha ,1.0);
local_weather.randomize_pos("Stratus",300.0,600.0,600.0,alpha);
local_weather.create_streak("Stratus",blat+get_lat(x,y,phi), blon+get_lon(x,y,phi), 2000.0+alt_offset+size_offset,300.0,40,1000.0,0.2,600.0, 10,1000.0,0.2,600.0,alpha ,1.0);
# and a Nimbus layer with precipitation
@ -1523,14 +1586,14 @@ local_weather.set_weather_station(blat, blon, 20000.0, 14.0, 12.0, 1005 * hp_to_
size_offset = 0.5 * m_to_ft * local_weather.cloud_vertical_size_map["Stratus_structured"];
local_weather.create_streak("Stratus (structured)",blat, blon, 4000.0+alt_offset+size_offset,22,0.0,0.3,22,0.0,0.3,0.0,1.0);
local_weather.randomize_pos("Stratus (structured)",1000.0,20000.0,20000.0,0.0);
local_weather.create_streak("Stratus (structured)",blat, blon, 4000.0+alt_offset+size_offset,1000.0,22,0.0,0.3,20000.0,22,0.0,0.3,20000.0,0.0,1.0);
local_weather.create_streak("Stratus (structured)",blat, blon, 6000.0+alt_offset+size_offset,16,0.0,0.4,16,0.0,0.4,0.0,1.0);
local_weather.randomize_pos("Stratus (structured)",1000.0,20000.0,20000.0,0.0);
local_weather.create_streak("Stratus (structured)",blat, blon, 7000.0+alt_offset+size_offset,11,0.0,0.5,11,0.0,0.5,0.0,1.0);
local_weather.randomize_pos("Stratus (structured)",1000.0,20000.0,20000.0,0.0);
local_weather.create_streak("Stratus (structured)",blat, blon, 6000.0+alt_offset+size_offset,1000.0,16,0.0,0.4,20000.0,16,0.0,0.4,20000.0,0.0,1.0);
local_weather.create_streak("Stratus (structured)",blat, blon, 7000.0+alt_offset+size_offset,1000.0,11,0.0,0.5,20000.0,11,0.0,0.5,20000.0,0.0,1.0);
tile_finished();
@ -1693,8 +1756,8 @@ calc_geo(blat);
local_weather.set_weather_station(blat, blon, 25000.0, 25.0, 22.0, 1013 * hp_to_inhg);
# then add some developing thunderstorms
local_weather.create_streak("Cumulonimbus (rain)",blat, blon, 3000.0+alt_offset,3,0.0,1.0,3,0.0,1.0,0.0,1.0);
local_weather.randomize_pos("Cumulonimbus (rain)",0.0,20000.0,20000.0,0.0);
local_weather.create_streak("Cumulonimbus (rain)",blat, blon, 3000.0+alt_offset,0.0,3,0.0,1.0,20000.0,3,0.0,1.0,20000.0,0.0,1.0);
# add overdeveloped convective clouds
@ -1752,25 +1815,25 @@ local_weather.set_weather_station(lat, lon, 18000.0, 20.0, 16.0, 29.75);
# now set up the clouds
x = 0.0; y = 0.0;
local_weather.create_cloud("Cirrus", "Models/Weather/cirrus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 28000.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 28000.0 + alt_offset,alpha);
x = 7500.0; y = 1000.0;
local_weather.create_cloud("Cirrus", "Models/Weather/cirrus2.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 28500.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrus2.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 28500.0 + alt_offset,alpha);
x = 16000.0; y = -1000.0;
local_weather.create_cloud("Cirrus", "Models/Weather/cirrus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 29500.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 29500.0 + alt_offset,alpha);
x = -16000.0; y = 2500.0;
local_weather.create_cloud("Cirrus", "Models/Weather/cirrus1.xml",blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 30000.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrus1.xml",blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 30000.0 + alt_offset,alpha);
x = 7000.0; y = 8000.0;
local_weather.create_cloud("Cirrocumulus", "Models/Weather/cirrocumulus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 22000.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrocumulus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 22000.0 + alt_offset,alpha);
x = -3000.0; y = 9000.0;
local_weather.create_cloud("Cirrocumulus", "Models/Weather/cirrocumulus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 21000.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrocumulus1.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 21000.0 + alt_offset,alpha);
x = -1000.0; y = 14000.0;
local_weather.create_cloud("Cirrocumulus", "Models/Weather/cirrocumulus2.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0 + alt_offset,alpha, 0);
compat_layer.create_cloud("Models/Weather/cirrocumulus2.xml", blat + get_lat(x,y,phi), blon+get_lon(x,y,phi), 20000.0 + alt_offset,alpha);
# add moderately strong convective clouds
@ -1945,27 +2008,24 @@ tile_finished();
var create_8_8_stratus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Stratus",lat, lon, alt,32,1250.0,0.0,32,1250.0,0.0,alpha,1.0);
local_weather.randomize_pos("Stratus",500.0,400.0,400.0,alpha);
local_weather.create_streak("Stratus",lat, lon, alt,500.0,32,1250.0,0.0,400.0,32,1250.0,0.0,400.0,alpha,1.0);
}
var create_8_8_cirrostratus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Cirrostratus",lat, lon, alt,30,1250.0,0.0,30,1250.0,0.0,alpha,1.0);
local_weather.randomize_pos("Cirrostratus",500.0,400.0,400.0,alpha);
local_weather.create_streak("Cirrostratus",lat,lon,alt,500.0,30,1250.0,0.0,400.0,30,1250.0,0.0,400.0,alpha,1.0);
}
var create_8_8_nimbus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Nimbus",lat, lon, alt,32,1250.0,0.0,32,1250.0,0.0,alpha,1.0);
local_weather.randomize_pos("Nimbus",500.0,200.0,200.0,alpha);
local_weather.create_streak("Nimbus",lat, lon, alt,500.0,32,1250.0,0.0,200.0,32,1250.0,0.0,200.0,alpha,1.0);
}
var create_6_8_stratus = func (lat, lon, alt, alpha) {
local_weather.create_streak("Stratus",lat, lon, alt,20,0.0,0.2,20,0.0,0.2,alpha,1.0);
local_weather.randomize_pos("Stratus",500.0,20000.0,20000.0,alpha);
local_weather.create_streak("Stratus",lat, lon, alt,500.0,20,0.0,0.2,20000.0,20,0.0,0.2,20000.0,alpha,1.0);
}
@ -1976,22 +2036,22 @@ var x = 2.0 * (rand()-0.5) * 15000;
var y = 2.0 * (rand()-0.5) * 15000;
var beta = rand() * 360.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,20,1200.0,0.3,12,1200.0,0.3,beta,1.2);
local_weather.randomize_pos("Stratus",500.0,400.0,400.0,beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,500.0,20,1200.0,0.3,400.0,12,1200.0,0.3,400.0,beta,1.2);
var x = 2.0 * (rand()-0.5) * 15000;
var y = 2.0 * (rand()-0.5) * 15000;
var beta = rand() * 360.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,18,1000.0,0.3,10,1000.0,0.3,beta,1.5);
local_weather.randomize_pos("Stratus",500.0,400.0,400.0,beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,500.0,18,1000.0,0.3,400.0,10,1000.0,0.3,400.0,beta,1.5);
var x = 2.0 * (rand()-0.5) * 15000;
var y = 2.0 * (rand()-0.5) * 15000;
var beta = rand() * 360.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,15,1000.0,0.3,18,1000.0,0.3,beta,2.0);
local_weather.randomize_pos("Stratus",500.0,400.0,400.0,beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,500.0,15,1000.0,0.3,400.0,18,1000.0,0.3,400.0,beta,2.0);
}
var create_4_8_stratus_patches = func (lat, lon, alt, alpha) {
@ -2003,8 +2063,8 @@ for (var i=0; i<16; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,4,950.0,0.2,6,950.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,4,950.0,0.2,500.0,6,950.0,0.2,500.0,alpha+beta,1.0);
}
}
@ -2018,8 +2078,8 @@ for (var i=0; i<16; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,4,950.0,0.2,6,950.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus (thin)",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,4,950.0,0.2,500.0,6,950.0,0.2,500.0,alpha+beta,1.0);
}
}
@ -2034,8 +2094,8 @@ for (var i=0; i<16; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,4,950.0,0.2,6,950.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus (structured)",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,4,950.0,0.2,500.0,6,950.0,0.2,500.0,alpha+beta,1.0);
}
}
@ -2050,8 +2110,8 @@ for (var i=0; i<6; i=i+1)
var x = 2.0 * (rand()-0.5) * 12000;
var y = 2.0 * (rand()-0.5) * 12000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Cirrostratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,4,2500.0,0.2,4,2500.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta);
local_weather.create_streak("Cirrostratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,4,2500.0,0.2,600.0,4,2500.0,0.2,600.0,alpha+beta,1.0);
}
}
@ -2065,10 +2125,10 @@ var y = 2.0 * (rand()-0.5) * 5000;
var tri = 1.5 + 1.5*rand();
var beta = (rand() -0.5) * 60.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y+4000,phi), lon+get_lon(x,y+4000,phi), alt,10,800.0,0.25,12,2800.0,0.15,alpha+90.0+beta,tri);
local_weather.randomize_pos("Stratus",500.0,400.0,600.0,alpha+90.0+beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y-4000,phi), alt,10,800.0,0.25,12,2800.0,0.15,alpha+270.0+beta,tri);
local_weather.randomize_pos("Stratus",500.0,400.0,600.0,alpha+270.0+beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y+4000,phi), lon+get_lon(x,y+4000,phi), alt,500.0,10,800.0,0.25,400.0,12,2800.0,0.15,600.0,alpha+90.0+beta,tri);
local_weather.create_streak("Stratus",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y-4000,phi), alt,500.0,10,800.0,0.25,400.0,12,2800.0,0.15,600.0,alpha+270.0+beta,tri);
}
var create_4_8_tstratus_undulatus = func (lat, lon, alt, alpha) {
@ -2079,10 +2139,10 @@ var y = 2.0 * (rand()-0.5) * 5000;
var tri = 1.5 + 1.5*rand();
var beta = (rand() -0.5) * 60.0;
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y+4000,phi), lon+get_lon(x,y+4000,phi), alt,10,800.0,0.25,12,2800.0,0.15,alpha+90.0+beta,tri);
local_weather.randomize_pos("Stratus (thin)",500.0,400.0,600.0,alpha+90.0+beta);
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y-4000,phi), alt,10,800.0,0.25,12,2800.0,0.15,alpha+270.0+beta,tri);
local_weather.randomize_pos("Stratus (thin)",500.0,400.0,600.0,alpha+270.0+beta);
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y+4000,phi), lon+get_lon(x,y+4000,phi), alt,500.0,10,800.0,0.25,400.0,12,2800.0,0.15,600.0,alpha+90.0+beta,tri);
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y-4000,phi), lon+get_lon(x,y-4000,phi), alt,500.0,10,800.0,0.25,400.0,12,2800.0,0.15,600.0,alpha+270.0+beta,tri);
}
var create_4_8_sstratus_undulatus = func (lat, lon, alt, alpha) {
@ -2093,8 +2153,34 @@ var y = 2.0 * (rand()-0.5) * 5000;
var tri = 1 + 1.5*rand();
var beta = (rand() -0.5) * 60.0;
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,20,900.0,0.25,12,2800.0,0.15,alpha+90.0+beta,tri);
local_weather.randomize_pos("Stratus (structured)",500.0,400.0,600.0,alpha+90.0+beta);
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,500.0,20,900.0,0.25,400.0,12,2800.0,0.15,600.0,alpha+90.0+beta,tri);
}
var create_4_8_cirrocumulus_bank = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
var x = 2.0 * (rand()-0.5) * 5000;
var y = 2.0 * (rand()-0.5) * 5000;
var tri = 1.4 + 0.6 *rand();
var beta = (rand() -0.5) * 60.0;
local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,900.0,15,750.0,0.2,400.0,24,750.0,0.2,400.0,alpha+90.0+beta,tri);
}
var create_4_8_cirrocumulus_undulatus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
var x = 2.0 * (rand()-0.5) * 5000;
var y = 2.0 * (rand()-0.5) * 5000;
var tri = 1.4 + 0.6 *rand();
var beta = (rand() -0.5) * 60.0;
local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,900.0,25,300.0,0.0,900.0,15,1400.0,0.0,300.0,alpha+90.0+beta,tri);
}
@ -2107,8 +2193,8 @@ for (var i=0; i<8; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,5,900.0,0.2,7,900.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,5,900.0,0.2,500.0,7,900.0,0.2,500.0,alpha+beta,1.0);
}
}
@ -2122,12 +2208,13 @@ for (var i=0; i<8; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,5,900.0,0.2,7,900.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus (thin)",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus (thin)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,5,900.0,0.2,500.0,7,900.0,0.2,500.0,alpha+beta,1.0);
}
}
var create_2_8_sstratus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
@ -2137,12 +2224,30 @@ for (var i=0; i<8; i=i+1)
var x = 2.0 * (rand()-0.5) * 18000;
var y = 2.0 * (rand()-0.5) * 18000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,5,900.0,0.2,7,900.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Stratus (structured)",300.0,500.0,500.0,alpha+beta);
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,5,900.0,0.2,500.0,7,900.0,0.2,500.0,alpha+beta,1.0);
}
}
var create_2_8_sstratus_streak = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
var x = 2.0 * (rand()-0.5) * 6000;
var y = 2.0 * (rand()-0.5) * 6000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Stratus (structured)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,100.0,20,1800.0,0.1,500.0,5,1700.0,0.0,500.0,alpha+beta,1.2);
}
var create_2_8_cirrostratus = func (lat, lon, alt, alpha) {
var phi = alpha * math.pi/180.0;
@ -2152,8 +2257,8 @@ for (var i=0; i<3; i=i+1)
var x = 2.0 * (rand()-0.5) * 12000;
var y = 2.0 * (rand()-0.5) * 12000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Cirrostratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,4,2300.0,0.2,4,2300.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Cirrostratus",300.0,600.0,600.0,alpha+beta);
local_weather.create_streak("Cirrostratus",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,4,2300.0,0.2,600.0,4,2300.0,0.2,600.0,alpha+beta,1.0);
}
}
@ -2167,8 +2272,8 @@ for (var i=0; i<3; i=i+1)
var x = 2.0 * (rand()-0.5) * 12000;
var y = 2.0 * (rand()-0.5) * 12000;
var beta = (rand() -0.5) * 180.0;
local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,3,4000.0,0.2,3,4000.0,0.2,alpha+beta,1.0);
local_weather.randomize_pos("Cirrocumulus (cloudlet)",300.0,1000.0,1000.0,alpha+beta);
local_weather.create_streak("Cirrocumulus (cloudlet)",lat+get_lat(x,y,phi), lon+get_lon(x,y,phi), alt,300.0,3,4000.0,0.2,1000.0,3,4000.0,0.2,1000.0,alpha+beta,1.0);
}
}
@ -2182,16 +2287,16 @@ var y = 2.0 * (rand()-0.5) * 10000;
var tri = 1.5 + 1.5*rand();
var beta = (rand() -0.5) * 60.0;
local_weather.create_streak("Cumulus",lat+get_lat(x,y+6000,phi), lon+get_lon(x,y+6000,phi), alt,15,600.0,0.2,20,600.0,0.2,alpha+90.0+beta,tri);
local_weather.randomize_pos("Cumulus",500.0,400.0,400.0,alpha+90.0+beta);
local_weather.create_streak("Cumulus",lat+get_lat(x,y-6000,phi), lon+get_lon(x,y-6000,phi), alt,15,600.0,0.2,20,600.0,0.2,alpha+270.0+beta,tri);
local_weather.randomize_pos("Cumulus",500.0,400.0,400.0,alpha+270.0+beta);
local_weather.create_streak("Cumulus",lat+get_lat(x,y+6000,phi), lon+get_lon(x,y+6000,phi), alt,500.0,15,600.0,0.2,400.0,20,600.0,0.2,400.0,alpha+90.0+beta,tri);
local_weather.create_streak("Cumulus",lat+get_lat(x,y-6000,phi), lon+get_lon(x,y-6000,phi), alt,500.0,15,600.0,0.2,400.0,20,600.0,0.2,400.0,alpha+270.0+beta,tri);
}
var create_cloud_bank = func (type, lat, lon, alt, x1, x2, height, n, alpha) {
local_weather.create_streak(type,lat,lon, alt+ 0.5* height,n,0.0,0.0,1,0.0,0.0,alpha,1.0);
local_weather.randomize_pos(type,height,x1,x2,alpha);
local_weather.create_streak(type,lat,lon, alt+ 0.5* height,height,n,0.0,0.0,x1,1,0.0,0.0,x2,alpha,1.0);
}
@ -2217,8 +2322,13 @@ var create_medium_thunderstorm = func(lat, lon, alt, alpha) {
var scale = 0.7 + rand() * 0.3;
local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 6000.0 * scale, 6000.0 * scale, 0.0, 1.0, 0.3, 1, 1.5);
local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
#local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
local_weather.create_hollow_layer("Stratus", lat, lon, alt+1500, 1000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 1.0, 0.3, 0.5);
local_weather.create_layer("Fog (thick)", lat, lon, alt+4000, 6000.0, 3400.0 * scale, 3400.0 * scale, 0.0, 1.5, 0.3, 0, 0.0);
local_weather.create_layer("Cumulonimbus (cloudlet)", lat, lon, alt+10000, 10000.0, 3600.0 * scale, 3600.0 * scale, 0.0, 1.2, 0.0, 0, 0.0);
# set the exclusion region for the Cumulus layer
@ -2232,17 +2342,26 @@ local_weather.create_effect_volume(1, lat, lon, 6000.0 * 0.7 * scale, 6000.0 * 0
var create_big_thunderstorm = func(lat, lon, alt, alpha) {
# var radius = 4000 + rand() * 4000;
var phi = alpha * math.pi/180.0;
var scale = 0.8;
local_weather.create_layer("Nimbus", lat, lon, alt, 500.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.25, 1, 1.5);
local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
#local_weather.create_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
local_weather.create_hollow_layer("Stratus", lat, lon, alt+1500, 1000.0, 7200.0 * scale, 7200.0 * scale, 0.0, 1.0, 0.3, 0.7);
local_weather.create_layer("Fog (thick)", lat, lon, alt+5000, 3000.0, 5500.0 * scale, 5500.0 * scale, 0.0, 0.7, 0.3, 0, 0.0);
local_weather.create_layer("Fog (thick)", lat+get_lat(0,-1000,phi), lon+get_lon(0,-1000,phi), alt+12000, 4000.0, 6300.0 * scale, 6300.0 * scale, 0.0, 0.7, 0.3, 0, 0.0);
local_weather.create_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
#local_weather.create_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-2000,phi), lon+get_lon(0,-2000,phi), alt+17000, 1000.0, 7500.0 * scale, 7500.0 * scale, 0.0, 1.0, 0.3, 0.5);
#local_weather.create_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0, 0.0);
local_weather.create_hollow_layer("Stratus", lat+get_lat(0,-3000,phi), lon+get_lon(0,-3000,phi), alt+20000, 1000.0, 9500.0 * scale, 9500.0 * scale, 0.0, 1.0, 0.3, 0.5);
local_weather.create_layer("Stratus (thin)", lat+get_lat(0,-4000,phi), lon+get_lon(0,-4000,phi), alt+24000, 1000.0, 11500.0 * scale, 11500.0 * scale, 0.0, 2.0, 0.3, 0, 0.0);
# set the exclusion region for the Cumulus layer

View file

@ -6,19 +6,19 @@
<name>local_weather</name>
<width>800</width>
<height>570</height>
<height>600</height>
<modal>false</modal>
<text>
<x>10</x>
<y>530</y>
<y>570</y>
<label>Place a single cloud</label>
</text>
<group>
<x>0</x>
<y>500</y>
<y>550</y>
<text>
<x>10</x>
@ -60,7 +60,7 @@
<group>
<x>0</x>
<y>470</y>
<y>525</y>
<combo>
@ -72,7 +72,9 @@
<property>/local-weather/tmp/scloud-type</property>
<value>Altocumulus</value>
<value>Cirrus</value>
<value>Cirrostratus</value>
<value>Cumulus</value>
<value>Cumulus (cloudlet)</value>
<value>Cumulonimbus</value>
<value>Fog (thin)</value>
<value>Fog (thick)</value>
@ -80,6 +82,7 @@
<value>Stratus</value>
<value>Stratus (structured)</value>
<value>Stratus (thin)</value>
<value>Test</value>
<binding>
<command>dialog-apply</command>
</binding>
@ -152,13 +155,13 @@
<text>
<x>10</x>
<y>440</y>
<y>485</y>
<label>Place a cloud streak</label>
</text>
<group>
<x>0</x>
<y>410</y>
<y>465</y>
<text>
<x>10</x>
@ -226,7 +229,7 @@
<group>
<x>0</x>
<y>380</y>
<y>440</y>
<combo>
@ -238,6 +241,7 @@
<property>/local-weather/tmp/cloud-type</property>
<value>Altocumulus</value>
<value>Cirrus</value>
<value>Cirrostratus</value>
<value>Cumulus</value>
<value>Cumulus (cloudlet)</value>
<value>Cumulonimbus</value>
@ -248,6 +252,7 @@
<value>Stratus</value>
<value>Stratus (structured)</value>
<value>Stratus (thin)</value>
<value>Test</value>
<binding>
<command>dialog-apply</command>
</binding>
@ -331,7 +336,7 @@
<group>
<x>0</x>
<y>340</y>
<y>410</y>
<text>
<x>10</x>
@ -394,13 +399,13 @@
<text>
<x>10</x>
<y>310</y>
<y>370</y>
<label>Start the convective system</label>
</text>
<group>
<x>0</x>
<y>270</y>
<y>345</y>
<text>
<x>10</x>
@ -463,13 +468,13 @@
<text>
<x>10</x>
<y>240</y>
<y>305</y>
<label>Create barrier clouds</label>
</text>
<group>
<x>0</x>
<y>210</y>
<y>280</y>
<text>
<x>10</x>
@ -567,13 +572,13 @@
<text>
<x>10</x>
<y>170</y>
<y>240</y>
<label>Place a cloud layer</label>
</text>
<group>
<x>0</x>
<y>140</y>
<y>220</y>
<text>
<x>10</x>
@ -641,7 +646,7 @@
<group>
<x>0</x>
<y>110</y>
<y>195</y>
<combo>
@ -653,6 +658,7 @@
<property>/local-weather/tmp/layer-type</property>
<value>Altocumulus</value>
<value>Cirrus</value>
<value>Cirrostratus</value>
<value>Cumulus</value>
<value>Cumulus (cloudlet)</value>
<value>Cumulonimbus</value>
@ -662,6 +668,7 @@
<value>Stratus</value>
<value>Stratus (structured)</value>
<value>Stratus (thin)</value>
<value>Test</value>
<binding>
<command>dialog-apply</command>
</binding>
@ -745,7 +752,7 @@
<button>
<x>10</x>
<y>70</y>
<y>160</y>
<legend>Create</legend>
<default>true</default>
<equal>true</equal>
@ -759,6 +766,195 @@
</button>
<text>
<x>10</x>
<y>130</y>
<label>Make a cloud box</label>
</text>
<group>
<x>0</x>
<y>110</y>
<text>
<x>10</x>
<y>0</y>
<label>x [m]</label>
</text>
<text>
<x>80</x>
<y>0</y>
<label>y [m]</label>
</text>
<text>
<x>150</x>
<y>0</y>
<label>alt [ft]</label>
</text>
<text>
<x>220</x>
<y>0</y>
<label>number</label>
</text>
<text>
<x>290</x>
<y>0</y>
<label>core frac.</label>
</text>
<text>
<x>360</x>
<y>0</y>
<label>core offset</label>
</text>
<text>
<x>430</x>
<y>0</y>
<label>core height</label>
</text>
<text>
<x>500</x>
<y>0</y>
<label>core num.</label>
</text>
<text>
<x>570</x>
<y>0</y>
<label>bottom size</label>
</text>
<text>
<x>640</x>
<y>0</y>
<label>bot. height</label>
</text>
<text>
<x>710</x>
<y>0</y>
<label>bot. num.</label>
</text>
</group>
<group>
<x>0</x>
<y>85</y>
<input>
<x>10</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-x-m</property>
</input>
<input>
<x>80</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-y-m</property>
</input>
<input>
<x>150</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-alt-ft</property>
</input>
<input>
<x>220</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-n</property>
</input>
<input>
<x>290</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-core-fraction</property>
</input>
<input>
<x>360</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-core-offset</property>
</input>
<input>
<x>430</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-core-height</property>
</input>
<input>
<x>500</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-core-n</property>
</input>
<input>
<x>570</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-bottom-fraction</property>
</input>
<input>
<x>640</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-bottom-thickness</property>
</input>
<input>
<x>710</x>
<y>0</y>
<width>65</width>
<height>25</height>
<property>/local-weather/tmp/box-bottom-n</property>
</input>
</group>
<button>
<x>10</x>
<y>50</y>
<legend>Create</legend>
<default>true</default>
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>nasal</command>
<script>local_weather.box_wrapper()</script>
</binding>
</button>
<!-- Button Box -->
@ -809,7 +1005,7 @@
</binding>
</button>
<!--<button>
<button>
<x>300</x>
<y>0</y>
<legend>Test</legend>
@ -818,7 +1014,7 @@
<command>nasal</command>
<script>local_weather.test()</script>
</binding>
</button>-->
</button>
</group>

View file

@ -2,24 +2,24 @@
<PropertyList>
<!-- Cloud placement methods -->
<!-- Weather tiles -->
<name>local_weather_tiles</name>
<width>300</width>
<width>310</width>
<height>330</height>
<modal>false</modal>
<text>
<x>10</x>
<y>270</y>
<label>Select a weather scenario tile</label>
<x>5</x>
<y>300</y>
<label>Select initial weather scenario tile</label>
</text>
<combo>
<x>10</x>
<y>270</y>
<y>275</y>
<width>280</width>
<height>25</height>
<live>true</live>
@ -31,58 +31,88 @@
<value>Low-pressure</value>
<value>Low-pressure-core</value>
<value>Cold-sector</value>
<value>Warm-sector</value>
<value>Coldfront</value>
<value>Warmfront-1</value>
<value>Warmfront-2</value>
<value>Warmfront-3</value>
<value>Warmfront-4</value>
<value>Warmfront</value>
<value>Tropical</value>
<value>---</value>
<!--<value>Altocumulus sky</value>-->
<!--<value>Broken layers</value>-->
<!--<value>Cirrus sky</value>-->
<!--<value>Fair weather</value>-->
<value>Glider's sky</value>
<value>Blue thermals</value>
<!--<value>Incoming rainfront</value>-->
<!--<value>8/8 stratus sky</value>-->
<value>Test tile</value>
<!--<value>Summer rain</value>-->
<!--<value>Test tile</value>-->
<binding>
<command>dialog-apply</command>
</binding>
</combo>
<text>
<x>10</x>
<y>220</y>
<label>orientation (deg)</label>
<x>5</x>
<y>240</y>
<label>wind deg</label>
</text>
<input>
<x>115</x>
<y>220</y>
<x>67</x>
<y>240</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/tile-orientation-deg</property>
</input>
<text>
<x>105</x>
<y>240</y>
<label>kt</label>
</text>
<input>
<x>125</x>
<y>240</y>
<width>30</width>
<height>25</height>
<property>/local-weather/tmp/windspeed-kt</property>
</input>
<text>
<x>155</x>
<y>220</y>
<y>240</y>
<label>alt. offset (ft)</label>
</text>
<input>
<x>240</x>
<y>220</y>
<y>240</y>
<width>50</width>
<height>25</height>
<property>/local-weather/tmp/tile-alt-offset-ft</property>
</input>
<text>
<x>10</x>
<x>5</x>
<y>210</y>
<label>wind model</label>
</text>
<combo>
<x>150</x>
<y>210</y>
<width>140</width>
<height>25</height>
<live>true</live>
<property>/local-weather/config/wind-model</property>
<value>constant</value>
<value>constant in tile</value>
<value>aloft interpolated</value>
<value>aloft waypoints</value>
<!--<value>airmass interpolated</value>-->
<binding>
<command>dialog-apply</command>
</binding>
</combo>
<text>
<x>5</x>
<y>180</y>
<label>tile selection mode</label>
</text>
@ -105,10 +135,10 @@
<checkbox>
<x>10</x>
<y>140</y>
<y>150</y>
<width>15</width>
<height>15</height>
<label>Terrain presampling</label>
<label>terrain presampling</label>
<property>/local-weather/tmp/presampling-flag</property>
<binding>
<command>dialog-apply</command>
@ -117,10 +147,10 @@
<checkbox>
<x>150</x>
<y>140</y>
<y>150</y>
<width>15</width>
<height>15</height>
<label>Worker threads</label>
<label>worker threads</label>
<property>/local-weather/tmp/thread-flag</property>
<binding>
<command>dialog-apply</command>
@ -129,7 +159,7 @@
<checkbox>
<x>10</x>
<y>110</y>
<y>125</y>
<width>15</width>
<height>15</height>
<label>asymmetric range</label>
@ -141,7 +171,7 @@
<checkbox>
<x>150</x>
<y>110</y>
<y>125</y>
<width>15</width>
<height>15</height>
<label>detailed clouds</label>
@ -151,6 +181,18 @@
</binding>
</checkbox>
<checkbox>
<x>10</x>
<y>100</y>
<width>15</width>
<height>15</height>
<label>dynamical weather</label>
<property>/local-weather/config/dynamics-flag</property>
<binding>
<command>dialog-apply</command>
</binding>
</checkbox>
<text>
<x>10</x>
<y>75</y>
@ -190,7 +232,7 @@
<x>0</x>
<y>0</y>
<legend>OK</legend>
<default>true</default>
<!--<default>true</default>-->
<equal>true</equal>
<binding>
<command>dialog-apply</command>
@ -206,10 +248,10 @@
<button>
<x>80</x>
<x>45</x>
<y>0</y>
<legend>Clear clouds</legend>
<default>true</default>
<!--<default>true</default>-->
<equal>true</equal>
<binding>
<command>nasal</command>
@ -218,7 +260,7 @@
</button>
<button>
<x>200</x>
<x>135</x>
<y>0</y>
<legend>Cancel</legend>
<equal>true</equal>
@ -228,6 +270,20 @@
</binding>
</button>
<button>
<x>200</x>
<y>0</y>
<legend>Show winds</legend>
<equal>true</equal>
<binding>
<command>dialog-show</command>
<dialog-name>local_weather_winds</dialog-name>
</binding>
<binding>
<command>dialog-apply</command>
</binding>
</button>
</group>

View file

@ -0,0 +1,354 @@
<?xml version="1.0"?>
<PropertyList>
<!-- Weather tiles -->
<name>local_weather_winds</name>
<width>500</width>
<height>200</height>
<modal>false</modal>
<text>
<x>10</x>
<y>170</y>
<label>Specify aloft wind layers</label>
</text>
<text>
<x>220</x>
<y>170</y>
<label>lat:</label>
</text>
<input>
<x>250</x>
<y>170</y>
<width>80</width>
<height>25</height>
<property>/local-weather/tmp/ipoint-latitude-deg</property>
</input>
<text>
<x>340</x>
<y>170</y>
<label>lon:</label>
</text>
<input>
<x>370</x>
<y>170</y>
<width>80</width>
<height>25</height>
<property>/local-weather/tmp/ipoint-longitude-deg</property>
</input>
<group>
<x>0</x>
<y>140</y>
<text>
<x>80</x>
<y>0</y>
<label>0</label>
</text>
<text>
<x>110</x>
<y>0</y>
<label>FL50</label>
</text>
<text>
<x>155</x>
<y>0</y>
<label>FL100</label>
</text>
<text>
<x>200</x>
<y>0</y>
<label>FL180</label>
</text>
<text>
<x>245</x>
<y>0</y>
<label>FL240</label>
</text>
<text>
<x>290</x>
<y>0</y>
<label>FL300</label>
</text>
<text>
<x>335</x>
<y>0</y>
<label>FL340</label>
</text>
<text>
<x>380</x>
<y>0</y>
<label>FL390</label>
</text>
<text>
<x>425</x>
<y>0</y>
<label>FL450</label>
</text>
</group>
<group>
<x>0</x>
<y>100</y>
<text>
<x>5</x>
<y>0</y>
<label>direction</label>
</text>
<input>
<x>70</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL0-wind-from-heading-deg</property>
</input>
<input>
<x>115</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL50-wind-from-heading-deg</property>
</input>
<input>
<x>160</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL100-wind-from-heading-deg</property>
</input>
<input>
<x>205</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL180-wind-from-heading-deg</property>
</input>
<input>
<x>250</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL240-wind-from-heading-deg</property>
</input>
<input>
<x>295</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL300-wind-from-heading-deg</property>
</input>
<input>
<x>340</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL340-wind-from-heading-deg</property>
</input>
<input>
<x>385</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL390-wind-from-heading-deg</property>
</input>
<input>
<x>430</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL450-wind-from-heading-deg</property>
</input>
</group>
<group>
<x>0</x>
<y>60</y>
<text>
<x>5</x>
<y>0</y>
<label>speed (kt)</label>
</text>
<input>
<x>70</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL0-windspeed-kt</property>
</input>
<input>
<x>115</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL50-windspeed-kt</property>
</input>
<input>
<x>160</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL100-windspeed-kt</property>
</input>
<input>
<x>205</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL180-windspeed-kt</property>
</input>
<input>
<x>250</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL240-windspeed-kt</property>
</input>
<input>
<x>295</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL300-windspeed-kt</property>
</input>
<input>
<x>340</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL340-windspeed-kt</property>
</input>
<input>
<x>385</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL390-windspeed-kt</property>
</input>
<input>
<x>430</x>
<y>0</y>
<width>40</width>
<height>25</height>
<property>/local-weather/tmp/FL450-windspeed-kt</property>
</input>
</group>
<group>
<x>10</x>
<y>10</y>
<button>
<x>0</x>
<y>0</y>
<legend>OK</legend>
<!--<default>true</default>-->
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>dialog-close</command>
</binding>
</button>
<button>
<x>45</x>
<y>0</y>
<legend>Set waypoint</legend>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>nasal</command>
<script>
if (local_weather.wind_model_flag == 5)
{local_weather.set_aloft_wrapper();}
</script>
</binding>
</button>
<button>
<x>150</x>
<y>0</y>
<legend>Clear Waypoints</legend>
<binding>
<command>nasal</command>
<script>
props.globals.getNode("local-weather/interpolation", 1).removeChildren("wind");
setprop("/local-weather/interpolation/ipoint-number",0);
</script>
</binding>
</button>
<button>
<x>265</x>
<y>0</y>
<legend>Cancel</legend>
<equal>true</equal>
<key>Esc</key>
<binding>
<command>dialog-close</command>
</binding>
</button>
<text>
<x>360</x>
<y>0</y>
<label>waypoints:</label>
</text>
<text>
<x>430</x>
<y>0</y>
<label>0</label>
<live>true</live>
<property>/local-weather/interpolation/ipoint-number</property>
</text>
</group>
</PropertyList>