<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.flightgear.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Wlbragg</id>
	<title>FlightGear wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.flightgear.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Wlbragg"/>
	<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/Special:Contributions/Wlbragg"/>
	<updated>2026-04-15T02:32:25Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.6</generator>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=User:Wlbragg&amp;diff=143873</id>
		<title>User:Wlbragg</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=User:Wlbragg&amp;diff=143873"/>
		<updated>2026-04-01T23:14:13Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Mission Generator Addon */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Kansas Scenery Three.jpg|632x632px|right|ElDorado, Kansas - ElDorado Lake]]&lt;br /&gt;
== Kansas/Midwest Scenery Development ==&lt;br /&gt;
&amp;lt;big&amp;gt;Hello and welcome to my Kansas/Midwest scenery development page.&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hopefully in the weeks, months and years to come, I can contribute some useful Midwest Scenery and Airport Data.&lt;br /&gt;
&lt;br /&gt;
=== Airport Layouts ===&lt;br /&gt;
So far, I have updated and submitted airport layouts to the [[FlightGear]] project for&lt;br /&gt;
*KEQA&lt;br /&gt;
*3AU&lt;br /&gt;
*KRCP&lt;br /&gt;
I have a few more in the works and plan to eventually add terminals and structures to as many Kansas Airports as I have the motivation to finish.&lt;br /&gt;
&lt;br /&gt;
[[File:KEQA_Airport.jpg|350px|KEQA]]&lt;br /&gt;
&lt;br /&gt;
=== Kansas Custom Scenery ===&lt;br /&gt;
I also have custom scenery generated from a combination of &lt;br /&gt;
*NLCD (land cover)&lt;br /&gt;
*OSM (roads, rivers and rail)&lt;br /&gt;
*SRTM (water data)&lt;br /&gt;
*New CS Data&lt;br /&gt;
*custom textures&lt;br /&gt;
for the entire state of [https://1drv.ms/u/s!AmL-8HrJKQ8ciRZl-sKR0HWnKPJQ?e=YuJoQ4 Kansas]&lt;br /&gt;
&lt;br /&gt;
This is currently very heavy data but works surprisingly well on my mid level CPU and graphics card.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery caption=&amp;quot;Kansas Scenery&amp;quot; mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
Kansas Scenery One.jpg|ElDorado, Kansas&lt;br /&gt;
Kansas Scenery Four.jpg|ElDorado Lake&lt;br /&gt;
Kansas Scenery Two.jpg|Kansas City&lt;br /&gt;
KICT.jpg|KICT - Wichita Dwight D. Eisenhower National Airport (Mid-Continent)&lt;br /&gt;
KIAB.jpg|KIAB - McConnell AFB, Wichita&lt;br /&gt;
Kansas.jpg|Kansas Sunset&lt;br /&gt;
KMCI.jpg|KMCI - Kansas City International Airport&lt;br /&gt;
ElDorado Lake Sunset.jpg|Sunset at ElDorado Lake&lt;br /&gt;
Cub Over Eldorado Lake.jpg|Cub at ElDorado Lake&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohio Custom Scenery ===&lt;br /&gt;
Custom scenery generated from a combination of &lt;br /&gt;
*CS Data based on NLCD (land cover)&lt;br /&gt;
*OSM (roads, rivers and rail)&lt;br /&gt;
*SRTM (water data to come)&lt;br /&gt;
*custom textures&lt;br /&gt;
for the entire state of [https://1drv.ms/u/s!AmL-8HrJKQ8ciAqGzBvP5O-kXZRz?e=1i4xWm Ohio].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery caption=&amp;quot;Ohio Scenery&amp;quot; mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
Cincinnati.jpg|KLUK - Cincinnati Municipal Airport&lt;br /&gt;
Toledo-Harbor View.jpg|Toledo - Harbor View&lt;br /&gt;
Lake_Erie_2.jpg|KCLE Cleveland-Hopkins International Airport&lt;br /&gt;
KPCW.jpg|KPCW - Carl R Keller Field&lt;br /&gt;
South Lorain.jpg|KCAK Akron-Canton Regional Airport&lt;br /&gt;
KBKL.jpg|KSKY - Griffing Sandusky Airport&lt;br /&gt;
Lake Erie.jpg|KCMH Port Columbus International Airport&lt;br /&gt;
KDAY_James_M._Cox_Dayton_International_Airport.jpg|KDAY James M. Cox Dayton International Airport&lt;br /&gt;
KYNG Youngstown-Warren Regional Airport.jpg|KYNG Youngstown - Warren Regional Airport&lt;br /&gt;
Near Youngstown.jpg|Near Youngstown&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Addon Development ==&lt;br /&gt;
[[File:Cargo-position.jpg|thumb|501x501px|Cargo placement UI.]]&lt;br /&gt;
&lt;br /&gt;
=== Cargo Towing Addon ===&lt;br /&gt;
Cargo Towing is now in the form of an addon. The Cargo Towing addon adds the ability for any helicopter to haul cargo from either a slung position. The AirCrane retains a docked cargo capability. Now included, hooks for adding load weight and winched rope length to any aircraft that has the instrumentation do display this data.. Using the Wildfire addon in addition to Cargo Towing addon, you can use a bambi bucket for firefighting. Also added a special feature that allows the AirCrane to load water from any of the 3 Water Tank cargos. The addon extended the sling load Cargo Towing capability to any aircraft.&lt;br /&gt;
&lt;br /&gt;
[https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Addons/CargoTowingAddon Cargo Towing Addon]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Intercept1.jpg|left|thumb|357x357px]]&lt;br /&gt;
&lt;br /&gt;
=== Mission Generator Addon ===&lt;br /&gt;
The Mission Generator addon is a continuing project that is designed to give FlightGear some ability to easily generate different mission orientated challenges. So far there are currently ... &lt;br /&gt;
&lt;br /&gt;
* Wildfire events. Using Wildfire and Cargo Towing addons you have the capability to put these fire out using various aircraft and methods. Including Water drop with tankers, bambi bucket or the Aircrane's jet spray boom apparatus.&lt;br /&gt;
* Search and rescue, water and land, including life flight to hospital.&lt;br /&gt;
* Intercept missions, including an airspace intrusion&lt;br /&gt;
&lt;br /&gt;
All missions are fully customizable and can be either on demand or set a random  range and time for the event, per event type.&lt;br /&gt;
&lt;br /&gt;
[https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Addons/MissionGenerator Mission Generator Addon]&lt;br /&gt;
&lt;br /&gt;
[[File:Intercept3.jpg|thumb|400x400px|Vectored to target.|left]]&lt;br /&gt;
[[File:Intercept2.jpg|left|thumb|408x408px|Target data vectoring.]][[File:Misson Generator Help.png|thumb|Help visual for the Mission Generator UI|border|center|820x820px]]&lt;br /&gt;
=== WildfireAddon ===&lt;br /&gt;
[[File:Bambi-bucket-full.jpg|thumb|761x761px|Bucket fills by submerging in any water source.]]The Wildfire addon originally was a FlightGear stand alone nasal module written and developed by Anders Gidenstam and incorporated in fgdata&lt;br /&gt;
&lt;br /&gt;
Credit:&lt;br /&gt;
&lt;br /&gt;
Wildfire - A cellular automaton forest fire model with the ability to spread over the multiplayer network.Written and developed by Anders Gidenstam  (anders(at)gidenstam.org) Copyright (C) 2007 - 2012  Anders Gidenstam  (anders(at)gidenstam.org) Modifications and conversion to Addon by Wayne Bragg wlbragg&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The cellular automata model used here is loosely based on A. Hernandez Encinas, L. Hernandez Encinas, S. Hoya White, A. Martin del Rey, G. Rodriguez Sanchez, &amp;quot;Simulation of forest fire fronts using cellular automata&amp;quot;, Advances in Engineering Software 38 (2007), pp. 372-378, Elsevier.&amp;lt;/blockquote&amp;gt;[https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Addons/Wildfire Wildfire Addon]&lt;br /&gt;
[[File:Bambi-bucket-dump.jpg|thumb|455x455px|Dumping the bucket will suppress the wildfire..|alt=|none]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Bambi-busket2.jpg|thumb|741x741px|View from engineering station.|alt=|none]]&lt;br /&gt;
&lt;br /&gt;
== Future Endeavors ==&lt;br /&gt;
&lt;br /&gt;
=== Surprise ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Iss4.jpg|512px|International Space Stay|alt=|left|thumb]][[File:Aircrane Over the Mojave.jpg|1024px|AirCrane over the Mojave Desert in the Palmdale/Landcaster area. Compositor shadows, OSM2CITY models, ALS lighting and Orthographics texture.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Aircrane Over the Mojave 2.jpg|1024px|AirCrane over the Mojave Desert in the Palmdale/Landcaster area. Compositor shadows, OSM2CITY models, ALS lighting and Orthographics texture.]]&lt;br /&gt;
{{-}}&lt;br /&gt;
&lt;br /&gt;
Watch this page for future releases of various project for [[FlightGear]]. I hope to have a couple of surprises just around the bend.&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=File:Misson_Generator_Help.png&amp;diff=143872</id>
		<title>File:Misson Generator Help.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=File:Misson_Generator_Help.png&amp;diff=143872"/>
		<updated>2026-04-01T23:11:08Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Wlbragg uploaded a new version of File:Misson Generator Help.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{int:filedesc}}==&lt;br /&gt;
{{Information&lt;br /&gt;
|description={{en|1=Help visual for the Mission Generator UI}}&lt;br /&gt;
|date=2026-04-01&lt;br /&gt;
|source={{own}}&lt;br /&gt;
|author=[[User:Wlbragg|Wlbragg]]&lt;br /&gt;
|permission=&lt;br /&gt;
|other versions=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{int:license-header}}==&lt;br /&gt;
{{self|cc-by-sa-4.0}}&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=User:Wlbragg&amp;diff=143870</id>
		<title>User:Wlbragg</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=User:Wlbragg&amp;diff=143870"/>
		<updated>2026-04-01T19:48:46Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Added a help image for the Mission Generator UI&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Kansas Scenery Three.jpg|632x632px|right|ElDorado, Kansas - ElDorado Lake]]&lt;br /&gt;
== Kansas/Midwest Scenery Development ==&lt;br /&gt;
&amp;lt;big&amp;gt;Hello and welcome to my Kansas/Midwest scenery development page.&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hopefully in the weeks, months and years to come, I can contribute some useful Midwest Scenery and Airport Data.&lt;br /&gt;
&lt;br /&gt;
=== Airport Layouts ===&lt;br /&gt;
So far, I have updated and submitted airport layouts to the [[FlightGear]] project for&lt;br /&gt;
*KEQA&lt;br /&gt;
*3AU&lt;br /&gt;
*KRCP&lt;br /&gt;
I have a few more in the works and plan to eventually add terminals and structures to as many Kansas Airports as I have the motivation to finish.&lt;br /&gt;
&lt;br /&gt;
[[File:KEQA_Airport.jpg|350px|KEQA]]&lt;br /&gt;
&lt;br /&gt;
=== Kansas Custom Scenery ===&lt;br /&gt;
I also have custom scenery generated from a combination of &lt;br /&gt;
*NLCD (land cover)&lt;br /&gt;
*OSM (roads, rivers and rail)&lt;br /&gt;
*SRTM (water data)&lt;br /&gt;
*New CS Data&lt;br /&gt;
*custom textures&lt;br /&gt;
for the entire state of [https://1drv.ms/u/s!AmL-8HrJKQ8ciRZl-sKR0HWnKPJQ?e=YuJoQ4 Kansas]&lt;br /&gt;
&lt;br /&gt;
This is currently very heavy data but works surprisingly well on my mid level CPU and graphics card.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery caption=&amp;quot;Kansas Scenery&amp;quot; mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
Kansas Scenery One.jpg|ElDorado, Kansas&lt;br /&gt;
Kansas Scenery Four.jpg|ElDorado Lake&lt;br /&gt;
Kansas Scenery Two.jpg|Kansas City&lt;br /&gt;
KICT.jpg|KICT - Wichita Dwight D. Eisenhower National Airport (Mid-Continent)&lt;br /&gt;
KIAB.jpg|KIAB - McConnell AFB, Wichita&lt;br /&gt;
Kansas.jpg|Kansas Sunset&lt;br /&gt;
KMCI.jpg|KMCI - Kansas City International Airport&lt;br /&gt;
ElDorado Lake Sunset.jpg|Sunset at ElDorado Lake&lt;br /&gt;
Cub Over Eldorado Lake.jpg|Cub at ElDorado Lake&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ohio Custom Scenery ===&lt;br /&gt;
Custom scenery generated from a combination of &lt;br /&gt;
*CS Data based on NLCD (land cover)&lt;br /&gt;
*OSM (roads, rivers and rail)&lt;br /&gt;
*SRTM (water data to come)&lt;br /&gt;
*custom textures&lt;br /&gt;
for the entire state of [https://1drv.ms/u/s!AmL-8HrJKQ8ciAqGzBvP5O-kXZRz?e=1i4xWm Ohio].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery caption=&amp;quot;Ohio Scenery&amp;quot; mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
Cincinnati.jpg|KLUK - Cincinnati Municipal Airport&lt;br /&gt;
Toledo-Harbor View.jpg|Toledo - Harbor View&lt;br /&gt;
Lake_Erie_2.jpg|KCLE Cleveland-Hopkins International Airport&lt;br /&gt;
KPCW.jpg|KPCW - Carl R Keller Field&lt;br /&gt;
South Lorain.jpg|KCAK Akron-Canton Regional Airport&lt;br /&gt;
KBKL.jpg|KSKY - Griffing Sandusky Airport&lt;br /&gt;
Lake Erie.jpg|KCMH Port Columbus International Airport&lt;br /&gt;
KDAY_James_M._Cox_Dayton_International_Airport.jpg|KDAY James M. Cox Dayton International Airport&lt;br /&gt;
KYNG Youngstown-Warren Regional Airport.jpg|KYNG Youngstown - Warren Regional Airport&lt;br /&gt;
Near Youngstown.jpg|Near Youngstown&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Addon Development ==&lt;br /&gt;
[[File:Cargo-position.jpg|thumb|501x501px|Cargo placement UI.]]&lt;br /&gt;
&lt;br /&gt;
=== Cargo Towing Addon ===&lt;br /&gt;
Cargo Towing is now in the form of an addon. The Cargo Towing addon adds the ability for any helicopter to haul cargo from either a slung position. The AirCrane retains a docked cargo capability. Now included, hooks for adding load weight and winched rope length to any aircraft that has the instrumentation do display this data.. Using the Wildfire addon in addition to Cargo Towing addon, you can use a bambi bucket for firefighting. Also added a special feature that allows the AirCrane to load water from any of the 3 Water Tank cargos. The addon extended the sling load Cargo Towing capability to any aircraft.&lt;br /&gt;
&lt;br /&gt;
[https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Addons/CargoTowingAddon Cargo Towing Addon]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Intercept1.jpg|left|thumb|357x357px]]&lt;br /&gt;
&lt;br /&gt;
=== Mission Generator Addon ===&lt;br /&gt;
The Mission Generator addon is a continuing project that is designed to give FlightGear some ability to easily generate different mission orientated challenges. So far there are currently ... &lt;br /&gt;
&lt;br /&gt;
* Wildfire events. Using Wildfire and Cargo Towing addons you have the capability to put these fire out using various aircraft and methods. Including Water drop with tankers, bambi bucket or the Aircrane's jet spray boom apparatus.&lt;br /&gt;
* Search and rescue, water and land, including life flight to hospital.&lt;br /&gt;
* Intercept missions, including an airspace intrusion&lt;br /&gt;
&lt;br /&gt;
All missions are fully customizable and can be either on demand or set a random  range and time for the event, per event type.&lt;br /&gt;
&lt;br /&gt;
[https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Addons/MissionGenerator Mission Generator Addon]&lt;br /&gt;
&lt;br /&gt;
[[File:Intercept3.jpg|thumb|400x400px|Vectored to target.|left]]&lt;br /&gt;
[[File:Intercept2.jpg|left|thumb|408x408px|Target data vectoring.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Misson Generator Help.png|thumb|Help visual for the Mission Generator UI|border|center|820x820px]]&lt;br /&gt;
=== WildfireAddon ===&lt;br /&gt;
[[File:Bambi-bucket-full.jpg|thumb|761x761px|Bucket fills by submerging in any water source.]]The Wildfire addon originally was a FlightGear stand alone nasal module written and developed by Anders Gidenstam and incorporated in fgdata&lt;br /&gt;
&lt;br /&gt;
Credit:&lt;br /&gt;
&lt;br /&gt;
Wildfire - A cellular automaton forest fire model with the ability to spread over the multiplayer network.Written and developed by Anders Gidenstam  (anders(at)gidenstam.org) Copyright (C) 2007 - 2012  Anders Gidenstam  (anders(at)gidenstam.org) Modifications and conversion to Addon by Wayne Bragg wlbragg&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The cellular automata model used here is loosely based on A. Hernandez Encinas, L. Hernandez Encinas, S. Hoya White, A. Martin del Rey, G. Rodriguez Sanchez, &amp;quot;Simulation of forest fire fronts using cellular automata&amp;quot;, Advances in Engineering Software 38 (2007), pp. 372-378, Elsevier.&amp;lt;/blockquote&amp;gt;[https://svn.code.sf.net/p/flightgear/fgaddon/trunk/Addons/Wildfire Wildfire Addon]&lt;br /&gt;
[[File:Bambi-bucket-dump.jpg|thumb|455x455px|Dumping the bucket will suppress the wildfire..|alt=|none]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Bambi-busket2.jpg|thumb|741x741px|View from engineering station.|alt=|none]]&lt;br /&gt;
&lt;br /&gt;
== Future Endeavors ==&lt;br /&gt;
&lt;br /&gt;
=== Surprise ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Iss4.jpg|512px|International Space Stay|alt=|left|thumb]][[File:Aircrane Over the Mojave.jpg|1024px|AirCrane over the Mojave Desert in the Palmdale/Landcaster area. Compositor shadows, OSM2CITY models, ALS lighting and Orthographics texture.]]&lt;br /&gt;
&lt;br /&gt;
[[File:Aircrane Over the Mojave 2.jpg|1024px|AirCrane over the Mojave Desert in the Palmdale/Landcaster area. Compositor shadows, OSM2CITY models, ALS lighting and Orthographics texture.]]&lt;br /&gt;
{{-}}&lt;br /&gt;
&lt;br /&gt;
Watch this page for future releases of various project for [[FlightGear]]. I hope to have a couple of surprises just around the bend.&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=File:Misson_Generator_Help.png&amp;diff=143869</id>
		<title>File:Misson Generator Help.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=File:Misson_Generator_Help.png&amp;diff=143869"/>
		<updated>2026-04-01T19:39:24Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Uploaded own work with UploadWizard&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{int:filedesc}}==&lt;br /&gt;
{{Information&lt;br /&gt;
|description={{en|1=Help visual for the Mission Generator UI}}&lt;br /&gt;
|date=2026-04-01&lt;br /&gt;
|source={{own}}&lt;br /&gt;
|author=[[User:Wlbragg|Wlbragg]]&lt;br /&gt;
|permission=&lt;br /&gt;
|other versions=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{int:license-header}}==&lt;br /&gt;
{{self|cc-by-sa-4.0}}&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=FlightGear_Newsletter_December_2025&amp;diff=143202</id>
		<title>FlightGear Newsletter December 2025</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=FlightGear_Newsletter_December_2025&amp;diff=143202"/>
		<updated>2025-12-09T04:39:13Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Re-Introducing the Rain Vector Editor Addon */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
NOTES TO EDITORS&lt;br /&gt;
&lt;br /&gt;
* Headings&lt;br /&gt;
  * DO NOT DELETE HEADINGS prior to final cleanup&lt;br /&gt;
  * Current headings and their order is merely a suggestion based on what have been used earlier&lt;br /&gt;
  * Changes made to headings or structure should also be copied the Newsletter template http://wiki.flightgear.org/User:Skybike/Template:This_months_newsletter/Newsletter_example&lt;br /&gt;
&lt;br /&gt;
* Final cleanup before write protecting&lt;br /&gt;
  * Remove unused headings&lt;br /&gt;
  * Remove {{Appendix}} if not used.&lt;br /&gt;
  * Update &amp;quot;Category: Changes after&amp;quot; to the FG version current at the 1st of this month&lt;br /&gt;
  * Finally remove this comment&lt;br /&gt;
  * Update [[Next Newsletter]] and [[FlightGear Newsletter]]&lt;br /&gt;
&lt;br /&gt;
* Discussion, issues and suggestions&lt;br /&gt;
  * Regarding this newsletter issue, please use the discussion page&lt;br /&gt;
  * Regarding the newsletter in general, primarily use the FlightGear Newsletter discussion page (Talk:FlightGear Newsletter)&lt;br /&gt;
  * Regarding this Newsletter template, please use FIXME&lt;br /&gt;
&lt;br /&gt;
+++   {{Newsletter-header|{{#time: F | 2025-05}}}}   +++&lt;br /&gt;
--&amp;gt;{{User:Skybike/Template:Newsletter-header-translate|2025-05}}&lt;br /&gt;
{{TOC_right|limit=2}}&lt;br /&gt;
&lt;br /&gt;
''We would like to emphasize that the monthly newsletter cannot live without the contributions of FlightGear users and developers. Everyone with a wiki account (free to register) is welcome to contribute to the newsletter.  If you know about any FlightGear related news or projects such as for example updated scenery or aircraft, please do feel invited to add such news to the newsletter.''&lt;br /&gt;
&lt;br /&gt;
''The new Visual Editor makes editing the wiki as simple as using a Word-processor, and even easier than using the forum as you don't even need to know the syntax for a url. Just hit the 'edit' link and start.''&lt;br /&gt;
&lt;br /&gt;
== Development news ==&lt;br /&gt;
&amp;lt;!-- News about FlightGear itself.  The FlightGear mailing list and/or core developers are a good source. --&amp;gt;&lt;br /&gt;
&amp;lt;!-- {{Disclaimer|id=final-fixed-function-release}}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Software tools and projects ==&lt;br /&gt;
=== Julia photoscenery generator GUI ===&lt;br /&gt;
==== Photoscenery-GUI ====&lt;br /&gt;
[[File:FG-JPS.png|thumb|278px|FG Julia Photoscenery-GUI Design proposal &amp;quot;LIBERA ET TESSELA&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
A major update has been released for the Photoscenery GUI, introducing significant accessibility enhancements.&lt;br /&gt;
&lt;br /&gt;
The key addition is full Internationalization (i18n) support, which makes the tool more accessible to a global audience. Users can now switch between languages instantly using new flag icons located in the control panel.&lt;br /&gt;
&lt;br /&gt;
What’s New:&lt;br /&gt;
&lt;br /&gt;
Multi-language Support:&lt;br /&gt;
The interface now supports multiple languages, including: English, Italian, French, German, Spanish, Portuguese, Chinese (Simplified), Japanese, Korean, Arabic (with full Right-to-Left layout support), and Russian.&lt;br /&gt;
&lt;br /&gt;
UI Improvements:&lt;br /&gt;
A new language switcher with flag icons has been implemented. Layout and font sizes have been optimized for better readability across different languages. Spacing in the control panel has been improved for a cleaner interface.&lt;br /&gt;
&lt;br /&gt;
This update is now available in the main branch on GitHub https://github.com/abassign/Photoscenery-GUI.git. Users are encouraged to test the new features and provide feedback regarding any translation errors or suggestions for additional language support.&lt;br /&gt;
&lt;br /&gt;
For more information, see the discussion on the FG Forum: https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=39066&amp;amp;start=420&lt;br /&gt;
&lt;br /&gt;
=== Re-Introducing the Rain Vector Editor Addon ===&lt;br /&gt;
This tool has been around for awhile, to reintroduce it there is a short video in the forum link below demonstrating its simple use. Use it to get the exact splash-x, y and z vectors that best look like rain on any specific window. Use that data to code dynamic control over the look of rain on windows and windscreens when introducing wind, prop blast and aircraft velocity through the air.&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/RainVectorEditor/&lt;br /&gt;
&lt;br /&gt;
https://forum.flightgear.org/viewtopic.php?f=14&amp;amp;t=43932&amp;amp;sid=20078e01f815391532dbded0a512a825&amp;amp;start=45#p436889&lt;br /&gt;
&lt;br /&gt;
== In the hangar ==&lt;br /&gt;
&amp;lt;!-- News about new and upgraded aircraft and related stuff. The official forum and other ones usually are a good source for this. --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === New aircraft === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Updated aircraft === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Liveries === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === Instruments === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === Aircraft reviews === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Scenery corner ==&lt;br /&gt;
&amp;lt;!-- Scenery development news --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Scenery Models === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Airports === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Land cover === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Osm2city === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === New OSM2City areas === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Interview with a contributor == --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Suggested flights == --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == AI == --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === AI traffic === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === AI scenarios === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Community news ==&lt;br /&gt;
&amp;lt;!-- === FlightGear on YouTube === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- embed video as {{#ev:youtube|VCc6PwRI1LA}}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Forum news === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Wiki updates === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Article of the month === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Multiplayer events ==&lt;br /&gt;
&amp;lt;!-- === Upcoming events === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Finished events === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == FlightGear events == --&amp;gt;&lt;br /&gt;
&amp;lt;!-- For example presence at FSWeekend --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Hardware reviews == --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Screenshot of the Month ==&lt;br /&gt;
&amp;lt;!--FlightGear's Screenshot of the Month {{#time: F | 2025-05}} 2025 is FIXME by {{usr|FIXME}}&lt;br /&gt;
ADD IMAGE --&amp;gt;&lt;br /&gt;
If you want to participate in the screenshot contest&amp;lt;!-- of {{#time: F | 2025-05 + 1month}}--&amp;gt;, you can submit your candidate to the {{forum link|title=this|f=88|t=}}. Be sure to see the first post for participation rules. For purposes of convenience and organization, at the end of the month or after 20 entries have been submitted, a new forum topic will be started containing all shots in an easy-to-view layout. The voting will then take place there.&amp;lt;!--Once the voting has finished, the best screenshot will be presented in the Newsletter edition of {{#time: F | 2025-05 + 1month}}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Changes after 2024.1]]&amp;lt;!--Has a new version been released this month? Use previous version!--&amp;gt;&lt;br /&gt;
[[Category:FlightGear Newsletter|2025 05]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--[[de:FlightGear Newsletter {{#time: F Y | 2025-05 | de }}]]--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=FlightGear_Newsletter_December_2025&amp;diff=143201</id>
		<title>FlightGear Newsletter December 2025</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=FlightGear_Newsletter_December_2025&amp;diff=143201"/>
		<updated>2025-12-09T04:37:36Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Rain Vector Editor Addon */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
NOTES TO EDITORS&lt;br /&gt;
&lt;br /&gt;
* Headings&lt;br /&gt;
  * DO NOT DELETE HEADINGS prior to final cleanup&lt;br /&gt;
  * Current headings and their order is merely a suggestion based on what have been used earlier&lt;br /&gt;
  * Changes made to headings or structure should also be copied the Newsletter template http://wiki.flightgear.org/User:Skybike/Template:This_months_newsletter/Newsletter_example&lt;br /&gt;
&lt;br /&gt;
* Final cleanup before write protecting&lt;br /&gt;
  * Remove unused headings&lt;br /&gt;
  * Remove {{Appendix}} if not used.&lt;br /&gt;
  * Update &amp;quot;Category: Changes after&amp;quot; to the FG version current at the 1st of this month&lt;br /&gt;
  * Finally remove this comment&lt;br /&gt;
  * Update [[Next Newsletter]] and [[FlightGear Newsletter]]&lt;br /&gt;
&lt;br /&gt;
* Discussion, issues and suggestions&lt;br /&gt;
  * Regarding this newsletter issue, please use the discussion page&lt;br /&gt;
  * Regarding the newsletter in general, primarily use the FlightGear Newsletter discussion page (Talk:FlightGear Newsletter)&lt;br /&gt;
  * Regarding this Newsletter template, please use FIXME&lt;br /&gt;
&lt;br /&gt;
+++   {{Newsletter-header|{{#time: F | 2025-05}}}}   +++&lt;br /&gt;
--&amp;gt;{{User:Skybike/Template:Newsletter-header-translate|2025-05}}&lt;br /&gt;
{{TOC_right|limit=2}}&lt;br /&gt;
&lt;br /&gt;
''We would like to emphasize that the monthly newsletter cannot live without the contributions of FlightGear users and developers. Everyone with a wiki account (free to register) is welcome to contribute to the newsletter.  If you know about any FlightGear related news or projects such as for example updated scenery or aircraft, please do feel invited to add such news to the newsletter.''&lt;br /&gt;
&lt;br /&gt;
''The new Visual Editor makes editing the wiki as simple as using a Word-processor, and even easier than using the forum as you don't even need to know the syntax for a url. Just hit the 'edit' link and start.''&lt;br /&gt;
&lt;br /&gt;
== Development news ==&lt;br /&gt;
&amp;lt;!-- News about FlightGear itself.  The FlightGear mailing list and/or core developers are a good source. --&amp;gt;&lt;br /&gt;
&amp;lt;!-- {{Disclaimer|id=final-fixed-function-release}}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Software tools and projects ==&lt;br /&gt;
=== Julia photoscenery generator GUI ===&lt;br /&gt;
==== Photoscenery-GUI ====&lt;br /&gt;
[[File:FG-JPS.png|thumb|278px|FG Julia Photoscenery-GUI Design proposal &amp;quot;LIBERA ET TESSELA&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
A major update has been released for the Photoscenery GUI, introducing significant accessibility enhancements.&lt;br /&gt;
&lt;br /&gt;
The key addition is full Internationalization (i18n) support, which makes the tool more accessible to a global audience. Users can now switch between languages instantly using new flag icons located in the control panel.&lt;br /&gt;
&lt;br /&gt;
What’s New:&lt;br /&gt;
&lt;br /&gt;
Multi-language Support:&lt;br /&gt;
The interface now supports multiple languages, including: English, Italian, French, German, Spanish, Portuguese, Chinese (Simplified), Japanese, Korean, Arabic (with full Right-to-Left layout support), and Russian.&lt;br /&gt;
&lt;br /&gt;
UI Improvements:&lt;br /&gt;
A new language switcher with flag icons has been implemented. Layout and font sizes have been optimized for better readability across different languages. Spacing in the control panel has been improved for a cleaner interface.&lt;br /&gt;
&lt;br /&gt;
This update is now available in the main branch on GitHub https://github.com/abassign/Photoscenery-GUI.git. Users are encouraged to test the new features and provide feedback regarding any translation errors or suggestions for additional language support.&lt;br /&gt;
&lt;br /&gt;
For more information, see the discussion on the FG Forum: https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=39066&amp;amp;start=420&lt;br /&gt;
&lt;br /&gt;
=== Re-Introducing the Rain Vector Editor Addon ===&lt;br /&gt;
This tool has been around for awhile, to reintroduce it here is a short video demonstrating its simple use. Use it to get the exact splash-x, y and z vectors that best look like rain on any specific window. Use that data to code dynamic control over the look of rain on windows and windscreens when introducing wind, prop blast and aircraft velocity through the air.&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/RainVectorEditor/&lt;br /&gt;
&lt;br /&gt;
https://forum.flightgear.org/viewtopic.php?f=14&amp;amp;t=43932&amp;amp;sid=20078e01f815391532dbded0a512a825&amp;amp;start=45#p436889&lt;br /&gt;
&lt;br /&gt;
== In the hangar ==&lt;br /&gt;
&amp;lt;!-- News about new and upgraded aircraft and related stuff. The official forum and other ones usually are a good source for this. --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === New aircraft === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Updated aircraft === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Liveries === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === Instruments === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === Aircraft reviews === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Scenery corner ==&lt;br /&gt;
&amp;lt;!-- Scenery development news --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Scenery Models === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Airports === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Land cover === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Osm2city === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === New OSM2City areas === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Interview with a contributor == --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Suggested flights == --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == AI == --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === AI traffic === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- === AI scenarios === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Community news ==&lt;br /&gt;
&amp;lt;!-- === FlightGear on YouTube === --&amp;gt;&lt;br /&gt;
&amp;lt;!-- embed video as {{#ev:youtube|VCc6PwRI1LA}}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Forum news === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Wiki updates === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Article of the month === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Multiplayer events ==&lt;br /&gt;
&amp;lt;!-- === Upcoming events === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- === Finished events === --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == FlightGear events == --&amp;gt;&lt;br /&gt;
&amp;lt;!-- For example presence at FSWeekend --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Hardware reviews == --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Screenshot of the Month ==&lt;br /&gt;
&amp;lt;!--FlightGear's Screenshot of the Month {{#time: F | 2025-05}} 2025 is FIXME by {{usr|FIXME}}&lt;br /&gt;
ADD IMAGE --&amp;gt;&lt;br /&gt;
If you want to participate in the screenshot contest&amp;lt;!-- of {{#time: F | 2025-05 + 1month}}--&amp;gt;, you can submit your candidate to the {{forum link|title=this|f=88|t=}}. Be sure to see the first post for participation rules. For purposes of convenience and organization, at the end of the month or after 20 entries have been submitted, a new forum topic will be started containing all shots in an easy-to-view layout. The voting will then take place there.&amp;lt;!--Once the voting has finished, the best screenshot will be presented in the Newsletter edition of {{#time: F | 2025-05 + 1month}}--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Changes after 2024.1]]&amp;lt;!--Has a new version been released this month? Use previous version!--&amp;gt;&lt;br /&gt;
[[Category:FlightGear Newsletter|2025 05]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--[[de:FlightGear Newsletter {{#time: F Y | 2025-05 | de }}]]--&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141735</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141735"/>
		<updated>2025-04-10T17:02:31Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/MissionGenerator  Mission Generator Addon] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/RainVectorEditor/  Rain Vector Editor] - Utility to allow dynamic editing of the rain vector for the rain effect on glass.&lt;br /&gt;
* [[Ramp Marshall]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=20572&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Nasal based Independant Ramp Marshall &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt;  omega95 - Wayne Bragg (wlbragg)&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40&lt;br /&gt;
  }}{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=42753&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing New Ramp Marshall Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author = &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40  &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Ramp Marshalling for FlightGear Airports.&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[Wildfire simulation]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39677&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Wildfire Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; AndersG - Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Wildfire simulation.&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141733</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141733"/>
		<updated>2025-04-10T16:53:22Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/MissionGenerator  Mission Generator Addon] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Ramp Marshall]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=20572&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Nasal based Independant Ramp Marshall &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt;  omega95 - Wayne Bragg (wlbragg)&amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40&lt;br /&gt;
  }}{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=42753&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing New Ramp Marshall Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author = &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40  &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Ramp Marshalling for FlightGear Airports.&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[Wildfire simulation]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39677&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Wildfire Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; AndersG - Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Wildfire simulation.&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141732</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141732"/>
		<updated>2025-04-10T16:51:03Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/MissionGenerator  Mission Generator Addon] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Ramp Marshall]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=20572&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Nasal based Independant Ramp Marshall &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt;  omega95 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40&lt;br /&gt;
  }}{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=42753&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing New Ramp Marshall Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt;  Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40  &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Ramp Marshalling for FlightGear Airports.&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[Wildfire simulation]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39677&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Wildfire Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; AndersG - Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Wildfire simulation.&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Wildfire_simulation&amp;diff=141731</id>
		<title>Wildfire simulation</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Wildfire_simulation&amp;diff=141731"/>
		<updated>2025-04-10T16:47:03Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A '''wildfire''' (woodland, grassland etc) '''simulation''' is available in FlightGear 2.0.0 and later.&lt;br /&gt;
&lt;br /&gt;
It is based on a cellular automata model of how wildfires spread. It is possible to set up fires that are shared (replicated) among users over MP for joint fire fighting efforts.&lt;br /&gt;
&lt;br /&gt;
[[File:Wildfire.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
[[File:Wildfire_dialog.jpg|thumb|133px]]&lt;br /&gt;
{{note|The wildfire simulation has been converted to an addon since 2020.3.19 and is available at [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Wildfire/ Wildfire Addon].|width=70%}}&lt;br /&gt;
&lt;br /&gt;
For older versions of FlightGear the wildfire simulation can be configured via the configuration dialog available from &amp;quot;Environment-&amp;gt;Wildfire settings&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
'''Enabled''': Fire simulation on/off. Cycling Enabled resets the fire simulation.&lt;br /&gt;
&lt;br /&gt;
'''Share over MP''': When set fire events, i.e. ignition and fire fighting, are sent/received over the MP network.&lt;br /&gt;
&lt;br /&gt;
'''Show 3d models''': Enable/disable rendering of the wildfire 3d models.&lt;br /&gt;
&lt;br /&gt;
'''Crash starts fire''': If your aircraft crashes somewhere inflammable there will be a fire.&lt;br /&gt;
&lt;br /&gt;
'''Report score''': Copilot reports success/failure of firefighting actions.&lt;br /&gt;
&lt;br /&gt;
'''Save on exit''': Save the fire event log when FlightGear exits. (Experimental)&lt;br /&gt;
&lt;br /&gt;
'''Load Wildfire log''': Load and execute a saved fire event log. (Experimental, can be very expensive)&lt;br /&gt;
&lt;br /&gt;
{{note|The fire and smoke models are particle systems so to see them Particle Systems must be enabled in the View-&amp;gt;Rendering Options dialog.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
{{warning|There is a hidden option called ''restore on startup'' that can be enabled via the property browser (or by a mischievous aircraft) and that will be saved when exiting FlightGear. On the next start of FlightGear the previous state of the Wildfire system will be loaded which can be very expensive indeed if the system is set to run the Wildfire module from the time stamps in the saved log to the current time (the same hold for Load Wildfire log, but there it should be clear to the user what happens). If this option gets enabled you can disable it by starting FlightGear with the command line option --prop:/environment/wildfire/restore-on-startup{{=}}0 and exit normally.}}&lt;br /&gt;
&lt;br /&gt;
== Fire fighting ==&lt;br /&gt;
The [[Consolidated Aircraft PBY Catalina]] air tanker / water bomber can be used to drop water on wildfires.&lt;br /&gt;
&lt;br /&gt;
== Developer information ==&lt;br /&gt;
See Docs/README.wildfire.&lt;br /&gt;
&lt;br /&gt;
== Known problems ==&lt;br /&gt;
&lt;br /&gt;
* Huge fires are expensive and cause significant fps drop.&lt;br /&gt;
* It appears the wildfire models are not removed properly from the scene graph. The frame rate impact (or part of) remains when 3d models are disabled and even after the Wildfire module has been reset.&lt;br /&gt;
* Wind is not considered.&lt;br /&gt;
* Crash starts fire does not consider the type of aircraft and the circumstances of the impact/crash, only the inflammability of the terrain.&lt;br /&gt;
* Prior to FG 2.4.0, to save the wildfire state you need to add the line &amp;quot;WRITE ALLOW [[$FG_HOME]]Wildfire/fire_log.xml&amp;quot; to $fgroot/Nasal/IOrules.  In FG 2.4.0 this is no longer needed.&lt;br /&gt;
* Sharing the wildfire state over the multiplayer network doesn't work in FlightGear 2.0.0. This is fixed in later versions.&lt;br /&gt;
&lt;br /&gt;
== External links ==&lt;br /&gt;
* [http://www.gidenstam.org/FlightGear/misc/WildFire/ A simple crash tender (fire truck) vehicle for FG 2.0.0 or later].&lt;br /&gt;
* [http://www.gidenstam.org/FlightGear/misc/WildFire/old/ Pre-CVS (FG 1.9.1) version].&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear feature]]&lt;br /&gt;
[[Category:Menubar]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Multiplayer]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Ramp_Marshall&amp;diff=141730</id>
		<title>Ramp Marshall</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Ramp_Marshall&amp;diff=141730"/>
		<updated>2025-04-10T16:16:41Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A '''(ramp) marshaller''' is a person who guides [[aircraft]] using hand (and wand) signals at a parking ramp. This is a new feature that being worked on for a more generalized scale in [[FlightGear]]. Until this gets merged into the fgdata repository, you can get this from omega95's fgdata clone or the new Singapore Scenery development. &lt;br /&gt;
&lt;br /&gt;
Currently, support can be added to airports with the [[UFO]], but soon ramp marshallers will be added automatically if a parking spot's flag is set to enable it. This will integrate FlightGear parking positions, ramp marshallers and other features that might come up related to parking ramps or gates.&lt;br /&gt;
&lt;br /&gt;
These are videos of the first prototype of the ramp marshaller.&lt;br /&gt;
&lt;br /&gt;
{{#ev:youtube | c_KEbyv_h5A}} {{#ev:youtube | Hz4WU8nsdls}}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== How to install the ramp marshaller ===&lt;br /&gt;
&lt;br /&gt;
NOTE: This work has been extended into a working addon called [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/RampMarshall/ Ramp Marshall Addon]. The addon is a continuation of this project and in no way meant to compete with this article. This is just a note for clarity that you can now just install the addon as you would any other addon and the functionality will be available. See: {{forum link|t=42753|title=Announcing New Ramp Marshall Addon}} (November 11, 2024). This article still needs updating to match the requirements and use of the new addon work.&lt;br /&gt;
&lt;br /&gt;
The new addon would not exist if not for the work of the developers mentioned throughout this article.&lt;br /&gt;
&lt;br /&gt;
The following steps are no longer needed. For now they have been left for reference to the original author's code.&lt;br /&gt;
# Download [https://github.com/RenanMsV/ramp-marshall this GitHub] and unzip it.&lt;br /&gt;
# Download this [https://archive.softwareheritage.org/browse/origin/directory/?origin_url=https://gitorious.org/fg/omega95s-fgdata.git&amp;amp;path=Nasal '''ramp_marshall.nas''']&lt;br /&gt;
# Put the '''Ramp''' Folder (can be found under Models/Airports in the GitHub) in $FG-ROOT\Models\Airport&lt;br /&gt;
# Put the [https://archive.softwareheritage.org/browse/origin/directory/?origin_url=https://gitorious.org/fg/omega95s-fgdata.git&amp;amp;path=Nasal '''ramp_marshall.nas'''] and the '''addon-main.nas''' (can be found unter the GitHub) in $FG-ROOT\Nasal&lt;br /&gt;
# Put every File and Folder wich is left now from the GitHub in your addons folder (see: [[Addon|how to install addons in FlightGear]])&lt;br /&gt;
&lt;br /&gt;
== Using the ramp marshaller ==&lt;br /&gt;
To use the ramp marshaller with a compatible aircraft at a compatible airport (scroll down to see how you can make aircraft and/or airports compatible), simply taxi to the gate/ramp you want to park at and click on the marshaller waiting at the ramp. His hands should move from rest position (both wands down by his sides) to direct you to the ramp. It's recommended to taxi to the ramp on the taxiway lines as long as you can see them and once they go out of view, follow the marshaller's instructions. Make sure that you're not taxiing faster than 5 to 10 kts at the ramp.&lt;br /&gt;
&lt;br /&gt;
Currently, the ramp marshaller knows the following signals -&lt;br /&gt;
* Move forward&lt;br /&gt;
* Turn left&lt;br /&gt;
* Turn right&lt;br /&gt;
* Slow down and stop&lt;br /&gt;
* Immediate stop&lt;br /&gt;
&lt;br /&gt;
But we're teaching him new signals. If you want to teach him a signal, scroll down to find out how. To find out what the signals look like, take a look at the videos above.&lt;br /&gt;
&lt;br /&gt;
== Adding ramp marshalling support ==&lt;br /&gt;
=== Adding ramp marshalling compatibility to aircraft ===&lt;br /&gt;
[[File:A330-200 Ramp Marshall Compatibility - getting position with ac3D.png|thumb|270px|Demonstrating how to get x-position of nose gear with AC3D for ramp marshalling compatibility.]]&lt;br /&gt;
&lt;br /&gt;
It's actually very simple to add ramp marshalling compatibility to an aircraft, the only file required to be edited is the aircraft's [[Aircraft-set.xml|&amp;lt;tt&amp;gt;-set.xml&amp;lt;/tt&amp;gt; file]] (or common xml file for aircraft groups). The user is still required to use some kind of 3D modeling program to get the position.&lt;br /&gt;
&lt;br /&gt;
# Open the aircraft's model file (containing the gears) with a 3D modeling program and get the '''x''' position of the center of the nose gear. (Or the position you want to rest at the ramp)&lt;br /&gt;
# Add the following code within the &amp;lt;code&amp;gt;&amp;lt;sim&amp;gt; ... &amp;lt;model&amp;gt; ... &amp;lt;/model&amp;gt;&amp;lt;/sim&amp;gt;&amp;lt;/code&amp;gt; tags in the aircraft's &amp;lt;tt&amp;gt;-set.xml&amp;lt;/tt&amp;gt; file. (or common xml file for aircraft groups)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;ramp&amp;gt;&lt;br /&gt;
    &amp;lt;x-m type=&amp;quot;float&amp;quot;&amp;gt;-15.9664&amp;lt;/x-m&amp;gt;&lt;br /&gt;
    &amp;lt;class type=&amp;quot;string&amp;quot;&amp;gt;2&amp;lt;/class&amp;gt;&lt;br /&gt;
&amp;lt;/ramp&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
:The '''x''' position from the 3D model is in the &amp;lt;code&amp;gt;&amp;lt;x-m&amp;gt;&amp;lt;/code&amp;gt; tag and the aircraft class is in the &amp;lt;code&amp;gt;&amp;lt;class&amp;gt;&amp;lt;/code&amp;gt; tag. With the current models, the aircraft classes are as follows -&lt;br /&gt;
:* 0 - 747s, 777-300s, A340s, A380s&lt;br /&gt;
:* 1 - 777-300s, A330-300s, 787s&lt;br /&gt;
:* 2 - A330-200s, 757s, 767s&lt;br /&gt;
:* 3 - A320s (includes A318s, A319s and A321s), 737s and other aircraft in general. This works for CRJs, E-jets etc.&lt;br /&gt;
&lt;br /&gt;
This should be all! The ramp marshaller should be able to ''recognize'' the aircraft and guide it to the right position at the ramp!&lt;br /&gt;
&lt;br /&gt;
=== Adding ramp marshallers to an airport ===&lt;br /&gt;
&lt;br /&gt;
'''NOTE -''' In the future, this will change because ramp marshall positions are going to be connected to an airport's '''groundnet.xml''' file.&lt;br /&gt;
&lt;br /&gt;
[[File:ramp-marshall-arpt-1.png|thumb|270px|Placing ramp marshaller using ufo]]&lt;br /&gt;
[[File:ramp-marshall-arpt-2.png|thumb|270px|Converting STG file to ramps XML]]&lt;br /&gt;
&lt;br /&gt;
# Start up FlightGear with the UFO at the airport you want to place ramp marshallers.&lt;br /&gt;
# Hit the {{key press|L}} key on the keyboard to bring up the model selection dialog and select &amp;lt;tt&amp;gt;[[$FG_ROOT]]/Models/Airport/Ramp/ramp.xml&amp;lt;/tt&amp;gt;. Using the ufo's scenery editing tools, place (and adjust) ramp marshallers everywhere you want. To make it simpler, click on the exact position you want the airplane to come to a stop. This will usually be at the end of a parking ramp's taxiway lines. Then, you will only have to change the heading.&lt;br /&gt;
# Once you've finished placing all the models, hit the {{key press|D}} key to dump the stg lines onto the console. (Do not close FlightGear yet) Copy the stg lines into a blank file. (name does not matter) And save it somewhere on your PC ($FG-HOME is a good place for that).&lt;br /&gt;
# Open up the Nasal console and execute the following function.&lt;br /&gt;
#: &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;ramp_marshall.convert_stg();&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Choose the stg file you pasted the dumped stg lines in to convert the file into a readable xml ramp position file. If you get following error: &amp;lt;code&amp;gt;(unauthorized directory - authorization no longer follows symlinks; to authorize reading additional directories, pass them to --allow-nasal-read)&amp;lt;/code&amp;gt;, type in Command line in the Launcher following phrase: &amp;lt;code&amp;gt;--allow-nasal-read=/path/to/directory&amp;lt;/code&amp;gt;. The converted file can be found &amp;lt;tt&amp;gt;[[$FG_HOME]]/Export&amp;lt;/tt&amp;gt; or under the Roaming folder in Windows.&lt;br /&gt;
# Copy the exported file (it will be called '''ramps-export.xml''') to &amp;lt;tt&amp;gt;[[$FG_ROOT]]/AI/Airports/&amp;lt;icao&amp;gt;/ramps.xml&amp;lt;/tt&amp;gt;. For example, if you want these ramps at Singapore Changi Intl. Airport (WSSS), you'd put it in &amp;lt;tt&amp;gt;[[$FG_ROOT]]/AI/Airports/WSSS/ramps.xml&amp;lt;/tt&amp;gt;. (you have to rename the '''ramps-export.xml''' into '''ramps.xml'''). If there is no folder for your Airport, just create a new one with the icao of your Airport.&lt;br /&gt;
&lt;br /&gt;
== Contributing to the module ==&lt;br /&gt;
&lt;br /&gt;
=== Teach the ramp marshaller new signals ===&lt;br /&gt;
&lt;br /&gt;
The marshaller's signal positions are stored in the '''ramp_marhsall.nas''' Nasal file in a hash. This is so that the signals can be accessed easily by other functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var pos = {&lt;br /&gt;
	rest: rmPos.new(lA_x:80, rA_x:-80),&lt;br /&gt;
	stop: rmPos.new(lA_x:-55, lAO_x:-70, rA_x:55, rAO_x:70),&lt;br /&gt;
	fwd_out: rmPos.new(lA_z:-50, lAO_x:-30,  rA_z:50, rAO_x:30),&lt;br /&gt;
	fwd_in:  rmPos.new(lA_z:-50, lAO_x:-120, rA_z:50, rAO_x:120),&lt;br /&gt;
	left_in:   rmPos.new(lAO_x:-120),&lt;br /&gt;
	left_out:  rmPos.new(lAO_x:-30),&lt;br /&gt;
	right_in:  rmPos.new(rAO_x:120),&lt;br /&gt;
	right_out: rmPos.new(rAO_x:30)&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* lA_x - Left Arm (shoulder joint) x axis rotation&lt;br /&gt;
* lA_y - Left Arm (shoulder joint) y axis rotation&lt;br /&gt;
* lA_z - Left Arm (shoulder joint) z axis rotation&lt;br /&gt;
* lAO_x - Left Arm Outer (elbow joint) x axis rotation&lt;br /&gt;
* lH_x - Left Hand x axis rotation&lt;br /&gt;
* lH_y - Left Hand y axis rotation&lt;br /&gt;
* lH_z - Left Hand x axis rotation&lt;br /&gt;
* rA_x - Right Arm (shoulder joint) x axis rotation&lt;br /&gt;
* rA_y - Right Arm (shoulder joint) y axis rotation&lt;br /&gt;
* rA_z - Right Arm (shoulder joint) z axis rotation&lt;br /&gt;
* rAO_x - Right Arm Outer (elbow joint) x axis rotation&lt;br /&gt;
* rH_x - Right Hand x axis rotation&lt;br /&gt;
* rH_y - Right Hand y axis rotation&lt;br /&gt;
* rH_z - Right Hand x axis rotation&lt;br /&gt;
&lt;br /&gt;
You can mess with the positions in a 3D modeling program or in FlightGear itself. Just look in the property tree under &amp;lt;tt&amp;gt;/airports/{icao}/ramps/ramp[n]&amp;lt;/tt&amp;gt; (find the correct 'n' index for the person last clicked on by looking at the &amp;lt;tt&amp;gt;/airports/active-ramp&amp;lt;/tt&amp;gt; property) and set the ramp marshaller's function to something random (so it isn't recognized) and change the values in the tree. Note: to get this to work properly, you first have to click on the nearest ramp marshaller, look up his index, and then click on a different marshaller so that the other marshaller's properties are not updated.&lt;br /&gt;
&lt;br /&gt;
If you would like to add a new hand position (say both hands out stright), then you'd add this to the bottom of the '''pos''' hash.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
     hands_out_front: rmPos.new(lA_z:-90, rA_z:90)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that one only needs to specify the rotations that are non-zero using hash syntax (key:value) in the arguments to the rmPos.new function. Remember to put a comma after the line right before the new one you add, since each line needs to end with a comma.&lt;br /&gt;
&lt;br /&gt;
Now, you can call this position as '''pos.hands_out_front''' to be used in the sequence function. Or if you'd like to get or set one of the position values.&lt;br /&gt;
&lt;br /&gt;
To add the signal sequence, add a new '''if''' state at the end of the last '''if(me.function == &amp;quot;&amp;lt;function_name&amp;gt;&amp;quot;''' statement with the sequence. You can use me.phase to control the sequence phase/step. &lt;br /&gt;
&lt;br /&gt;
Then, add the signal sequence. &lt;br /&gt;
&lt;br /&gt;
HINT - To animate the marshaller to a position you specified and then to go to the next step when it's complete and be achieved like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
if(me.phase == 0) {&lt;br /&gt;
    if(!check_pos(ramp_tree, pos.hands_out_front)) {&lt;br /&gt;
	full_animate(ramp_tree, pos.hands_out_front, 100);&lt;br /&gt;
    } else {&lt;br /&gt;
        me.phase = 1;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above code checks if the marshallers is not in the '''hands_out_front''' pos already and if he isn't, he'll move his hands to that position. In the full_animate function, you can specify the speed his hands should move at in ''degrees per second''. In this case, he'll move his hands forward to the specified position at 100 degrees per second.&lt;br /&gt;
&lt;br /&gt;
==== Useful internal functions ====&lt;br /&gt;
&lt;br /&gt;
* '''rmPos.new(lA_x, lA_z, lAO_x, rA_x, rA_z, rAO_x)''' creates and returns a new instance of the rmPos hash. This hash is in the '''pos.&amp;lt;pos_name&amp;gt;''' format and can be used in the functions below.&lt;br /&gt;
&lt;br /&gt;
* '''getDeviation(heading, target)''' returns the deviation to a target heading from your current heading. This is used by the marshaller to help guide you to the ramp.&lt;br /&gt;
&lt;br /&gt;
* '''animate(property, target, rate)''' animates a property (slowly changes the property value) to a target value at the given rate. (Rate is in degrees per second)&lt;br /&gt;
&lt;br /&gt;
* '''get_pos_values(ramp_tree)''' returns the current position of the marshaller's arms in a hash similar to that of the pos.&amp;lt;pos_name&amp;gt; hash.&lt;br /&gt;
&lt;br /&gt;
* '''check_pos(ramp_tree, target) returns true if the marshaller is at a given position. '''target''' must be a hash similar to the '''pos.&amp;lt;pos_name&amp;gt;''' hash. It is best to simply send a position specified in the '''pos''' hash.&lt;br /&gt;
&lt;br /&gt;
* '''full_animate(ramp_tree, pos_hash, rate) animates the ramp marshaller to the given position at the given rate.&lt;br /&gt;
&lt;br /&gt;
* '''load_ramps(icao)''' loads all ramps (and marshallers) at the given airport.&lt;br /&gt;
&lt;br /&gt;
'''NOTE -''' If any of the functions has '''ramp_tree''' as an argument and the function is being used within the ''main_loop'', the variable '''ramp_tree''' must be passed as an argument.&lt;br /&gt;
&lt;br /&gt;
=== Using different models or textures ===&lt;br /&gt;
&lt;br /&gt;
Soon, new features will becoming available which lets the ramp marshaller model be specified in the position config file.&lt;br /&gt;
&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
== Further development ==&lt;br /&gt;
&lt;br /&gt;
=== Visual Docking Guidance System ===&lt;br /&gt;
&lt;br /&gt;
===Prototype for a Visual Docking Guidance System (VDGS), entirely written in xml===&lt;br /&gt;
{{#ev:youtube | fgG55zZQx-k}}&lt;br /&gt;
&lt;br /&gt;
==Related content==&lt;br /&gt;
===Wiki articles===&lt;br /&gt;
*[[Walk view]]&lt;br /&gt;
&lt;br /&gt;
===Forum topics===&lt;br /&gt;
*{{forum link|t=20572|title=Nasal based Independant Ramp Marshall}} (August 2013-Mars 2018)&lt;br /&gt;
&lt;br /&gt;
===Source code===&lt;br /&gt;
*[https://archive.softwareheritage.org/browse/origin/directory/?origin_url=https://gitorious.org/fg/omega95s-fgdata.git Omega95's fgdata Gitorious archive] archived from the {{gitorious source|proj=fg|repo=omega95s-fgdata|text=original}} Mars 30, 2016.&lt;br /&gt;
*[https://archive.softwareheritage.org/browse/origin/?origin_url=https%3A%2F%2Fgitorious.org%2Ffg-singapore-scenery%2Ffg-singapore-devel.git FG Singapore Scenery Gitorious archive] archived from the {{gitorious source|proj=fg-singapore-scenery|repo=fg-singapore-devel|text=original}} Mars 30, 2016.&lt;br /&gt;
&lt;br /&gt;
[[Category:Scenery enhancement]]&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141729</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141729"/>
		<updated>2025-04-10T15:39:11Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/MissionGenerator  Mission Generator Addon] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141728</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141728"/>
		<updated>2025-04-10T15:38:12Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/MissionGenerator/] Mission Generator Addon] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141727</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141727"/>
		<updated>2025-04-10T15:37:06Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/MissionGenerator/]:Mission Generator Addon] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141725</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141725"/>
		<updated>2025-04-10T15:32:54Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Mission Generator Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141724</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141724"/>
		<updated>2025-04-10T15:31:13Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Mission Generator Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141723</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141723"/>
		<updated>2025-04-10T15:30:00Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Mission Generator Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=89&amp;amp;t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Announcing Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141722</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141722"/>
		<updated>2025-04-10T15:28:39Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Mission Generator Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=89&amp;amp;t=39774&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141721</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141721"/>
		<updated>2025-04-10T15:24:11Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Add Mission Generator Addon&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Mission Generator Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=89&amp;amp;t=39774&amp;amp;hilit=Mission+Generator&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Mission Generator Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Generate various random missions - Search &amp;amp; Rescue, Life Flight, Aerial Intrusion, Wildfire.&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141720</id>
		<title>Addon</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Addon&amp;diff=141720"/>
		<updated>2025-04-10T15:07:55Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* List of Addons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File:Fgaddonslogo202x89.png|right]] &lt;br /&gt;
To make it easier to create '''addons''' there is since FlightGear 2017.3 a new way to create addons.  In essence FlightGear will load an overlay XML into the property tree and start a well known Nasal function.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314620#p314620 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We now have a simple way to add addons to FlightGear without the need to mess around with &amp;lt;code&amp;gt;FGData/Nasal&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;FGHome/Nasal&amp;lt;/code&amp;gt; directories.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314563#p314563 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; New Feature: Addon - &amp;quot;API&amp;quot; &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 18th, 2017 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{TOC limit|3}}&lt;br /&gt;
&lt;br /&gt;
== Installing and using an addon ==&lt;br /&gt;
Download and copy the addon to a directory on your computer.&lt;br /&gt;
&lt;br /&gt;
If you use the launcher, select the Add-ons page from the icon bar on the left, then find the section Add-on Module folders and click the Add(+) button. Select the folder where you put the addon. Once the addon is shown in the list, make sure its check box is selected.&lt;br /&gt;
&lt;br /&gt;
Or alternatively, use the command line switch &amp;lt;code&amp;gt;--addon=/path/to/some/addon&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; if you are not using the launcher.&lt;br /&gt;
&lt;br /&gt;
== Creating an addon ==&lt;br /&gt;
There is a very simple Skeleton addon available in FGAddon to be used as a template.&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt; See {{fgaddon source|path=Addons/Skeleton}}.&lt;br /&gt;
&lt;br /&gt;
A leading slash (&amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;) in this section indicates the base directory of the directory structure of the addon.&lt;br /&gt;
&lt;br /&gt;
=== Minimum configuration ===&lt;br /&gt;
An addon may be installed in a directory anywhere on your hard disk and need at least two files:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-config.xml&amp;lt;/code&amp;gt; - A standard [[PropertyList XML files|PropertyList XML file]] to be used to populate or modify the [[property tree]]. (Same as to be used in &amp;lt;code&amp;gt;--config=foo.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-main.nas&amp;lt;/code&amp;gt; - The Nasal hook for the logic. This file needs a function called &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; which will be called from the global addon initializer (&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Additional common files ===&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-metadata.xml&amp;lt;/code&amp;gt; - A PropertyList XML file with metadata about the addon it.&lt;br /&gt;
* &amp;lt;code&amp;gt;/addon-menubar-items.xml&amp;lt;/code&amp;gt; - A PropertyList XML file describing menus to be added to the FlightGear menu bar.&lt;br /&gt;
* &amp;lt;code&amp;gt;/gui/dialogs/&amp;amp;lt;my-foobar-dialog&amp;amp;gt;.xml&amp;lt;/code&amp;gt; - PropertyList XML files to create custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Good to know ===&lt;br /&gt;
The new addon mechanism lets you add a &amp;lt;code&amp;gt;addon-config.xml&amp;lt;/code&amp;gt; to override the settings in &amp;lt;code&amp;gt;defaults.xml&amp;lt;/code&amp;gt; and other files.&lt;br /&gt;
&lt;br /&gt;
That will allow an addon to&lt;br /&gt;
*  Override key bindings (as in the spoken ATC addon)&lt;br /&gt;
*  Add or override autopilots and property rules&lt;br /&gt;
*  Introduce XML state machines&lt;br /&gt;
&lt;br /&gt;
Unless your really want to add/change/remove those at runtime, this should cater for most use cases.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314902#p314902 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 23rd, 2017 &lt;br /&gt;
  |added  =  Jul 23rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We have some instructions how to use SVN [[FGAddon|in our wiki]]. It covers mostly aircraft development but the workflow is pretty much the same for addons.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314647#p314647 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Spoken ATC &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sound Support ===&lt;br /&gt;
Sound support is available using fgcommand's.&lt;br /&gt;
&lt;br /&gt;
See [[Nasal FAQ]] &amp;quot;play-audio-sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
https://sourceforge.net/p/flightgear/flightgear/ci/5acf2e26d085b7553b2387b9753e9253e8b4bff4&lt;br /&gt;
&lt;br /&gt;
[[Hackathon Proposal:Addon specific Sound Queues]]&lt;br /&gt;
&lt;br /&gt;
== Addon initialization ==&lt;br /&gt;
On initialization fgfs takes care of:&lt;br /&gt;
* Through {{flightgear source|path=src/Main/options.cxx|text=&amp;lt;code&amp;gt;options.cxx&amp;lt;/code&amp;gt;}}:&amp;lt;ref name=&amp;quot;Forum_announcement&amp;quot;/&amp;gt;&lt;br /&gt;
** Creating a property under &amp;lt;code&amp;gt;/addons/addon[n]/path=/path/to/some/addon&amp;lt;/code&amp;gt;&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt; into the property tree (same as &amp;lt;code&amp;gt;--config=/path/to/some/addon/addon-config.xml&amp;lt;/code&amp;gt;)&lt;br /&gt;
** Adding &amp;lt;code&amp;gt;/path/to/some/addon&amp;lt;/code&amp;gt; to the list of allowed directories (same as &amp;lt;code&amp;gt;--fg-aircraft=/path/to/some/addon&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Through {{fgdata source|path=Nasal/addons.nas|text=&amp;lt;code&amp;gt;addons.nas&amp;lt;/code&amp;gt;}}:&lt;br /&gt;
** Loading &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt; into namespace &amp;lt;code&amp;gt;__addon[ADDON_ID]__&amp;lt;/code&amp;gt;&lt;br /&gt;
** Calling &amp;lt;code&amp;gt;main(addonGhost)&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;/foo/bar/baz/addon-main.nas&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Aircraft specific config (addon-hints) ==&lt;br /&gt;
Some addons need per-aircraft configuration. While addons should strive to be self-contained (ie. the addon should contain means to&lt;br /&gt;
detect different aircraft and apply config from whithin the addon) there is also the possibility for the addon to read so called &amp;quot;addon-hints&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
For this, a special property tree exists: &amp;lt;code&amp;gt;/sim/addon-hints/&amp;lt;addon&amp;gt;/...&amp;lt;/code&amp;gt;&lt;br /&gt;
The tree is expected to be populated from the aircraft.xml file. The addon can then read the properties from a common place regardless of loaded aircraft.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;sim&amp;gt;&lt;br /&gt;
  &amp;lt;addon-hints&amp;gt;&lt;br /&gt;
    &amp;lt;my-addon&amp;gt;&lt;br /&gt;
        ...&lt;br /&gt;
    &amp;lt;/my-addon&amp;gt;&lt;br /&gt;
  &amp;lt;/addon-hints&amp;gt;&lt;br /&gt;
&amp;lt;/sim&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== APIs ==&lt;br /&gt;
{{hatnote|For more details about these APIs, see the readme file, {{readme file|add-ons}}.}}&lt;br /&gt;
=== C++ API ===&lt;br /&gt;
There is a C++ API on FlightGear's side that handle some on the addon related tasks manly through the &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Addon()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AddonVersion()&amp;lt;/code&amp;gt; classes.&lt;br /&gt;
&lt;br /&gt;
=== Nasal API ===&lt;br /&gt;
The Nasal add-on API lives in the 'addons' namespace and can for example do queries to &amp;lt;code&amp;gt;AddonManager()&amp;lt;/code&amp;gt; and read data from &amp;lt;code&amp;gt;addons.Addon&amp;lt;/code&amp;gt; objects.  It can for example compare addon versions if there is dependencies.&lt;br /&gt;
&lt;br /&gt;
See: {{Repo link|site=gitlab|proj=flightgear|repo=fgdata|branch=next|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{See also|Howto:Creating a simple modding framework}}&lt;br /&gt;
&lt;br /&gt;
ATC chatter was removed in 2015, see fgdata commit: [https://sourceforge.net/p/flightgear/fgdata/ci/81607f734e13add9be02816ddaec305d05bc4e47/ 81607f734e13add9be02816ddaec305d05bc4e47]&lt;br /&gt;
&lt;br /&gt;
And the devel list messages referenced in the commit log.&lt;br /&gt;
the other relevant commit is this: b60736ba7add2a7cd39af3d8a974d5be3ea46e8b&lt;br /&gt;
It would not be very difficult to restore this functionality, or even generalize/improve it significantly (which was the scope of the original discussion on the devel list)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288388#p288388 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The restored functionality could be distributed as a tarball that is extracted into $FG_ROOT - alternatively, into $FG_HOME, because Nasal files there are treated as overlays, which basically means that you can install user-specific extensions there without having to tamper with $FG_ROOT, it would only take  very minor changes to turn the chatter feature into a corresponding &amp;quot;addon&amp;quot; - not unlike fgcamera ...&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=288392#p288392 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Whatever happened to radom radio  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Hooray &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jun 11th, 2016 &lt;br /&gt;
  |added  =  Jun 11th, 2016 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We should absolutely stop telling anyone to edit preferences.xml in FG_ROOT; any documentation or advice which says to should be changes ASAP. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=192632#p192632 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: NavCache:init failed:Sqlite error:Sqlite API abuse &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; zakalawe &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 26th, 2013 &lt;br /&gt;
  |added  =  Oct 26th, 2013 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As of 12/2017, the addon API is in the process of being significantly updated &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36146017/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150159/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36150444/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of Addons ==&lt;br /&gt;
You can find the official repository at {{fgaddon source|path=Addons}}&lt;br /&gt;
&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-hudheli Additional Heli HUD's] - ([https://github.com/slawekmikula/flightgear-addon-hudheli/blob/master/doc/manual.md manual]) - encapsulation of HeliHUD package as an addon&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-aerotow-everywhere Aerotow Everywhere] AI towing aircraft for gliders at every airport.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=40742&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Aerotow Everywhere &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 14th, 2022&lt;br /&gt;
  |added  =  Aug 14th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/SP-NTX/AnotherGUI AnotherGUI] - An add-on that adds a new GUI style.&lt;br /&gt;
* ATC Chatter (ported by Torsten) {{progressbar|100}}&lt;br /&gt;
* [[Blacklist add-on]] - Automatically ignore pilots with certain callsigns or multiplayer models.&lt;br /&gt;
* [[Cargo Towing Addon]] &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=36824&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Cargo Towing Addon &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Wayne Bragg (wlbragg) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  &lt;br /&gt;
  |added  =  &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - Allow any helicopter to tow, move and place various types of models.&lt;br /&gt;
* cockpit-view (work in progress by wkitty42)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316498#p316498 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; wkitty42 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 13th, 2017 &lt;br /&gt;
  |added  =  Aug 13th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Earthview#Customization]] - High resolution customization&lt;br /&gt;
* [[FaceTrackNoIR]] (ported by HHS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36454826/&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Unknown, HHS&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   =  Nov 1th, 2018 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt; - An addon to interface this [[Head tracking|head tracker]] with FlightGear&lt;br /&gt;
* [https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792 Fencemaker] (Eases creating Fence-like scenery objects. Originally by VaLeo, converted to an addon by sfr) - ([https://www.mediafire.com/file/cf0la63v9g352md/fencemaker_addon.zip/file download])&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    = https://forum.flightgear.org/viewtopic.php?f=5&amp;amp;t=24792&amp;amp;start=45#p390066&lt;br /&gt;
  |title  = &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |author = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |maintainer = &amp;lt;nowiki&amp;gt; Stefan Frank &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  |date   = Aug 8th, 2021&lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-fgcamera FGCamera] - ([https://github.com/PlayeRom/flightgear-addon-fgcamera/blob/master/Docs/manual.md manual]) - [[FGCamera | Wiki Page]]&lt;br /&gt;
* [[FGPlot]]&lt;br /&gt;
* [[Ground Services]] (ported by ThomasS)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=316400#p316400 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; ThomasS &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Aug 12th, 2017 &lt;br /&gt;
  |added  =  Aug 12th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* {{fgaddon source|path=Addons/Headtracker/|text=Headtracker addon}} Helps integrate FaceTrackNoIR and opentrack with FlightGear.&lt;br /&gt;
* [[HighAirTrader]] An in-sim mini game in which you transport goods to different airports.&lt;br /&gt;
* [https://github.com/tdammers/fg-hoppie-acars Hoppie ACARS client] - connect to [http://www.hoppie.nl/acars Hoppie's ACARS], used on VATSIM and other networks.&lt;br /&gt;
* [https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Addons/Illuminator/ Illuminator] - configure lights attached to 3D models (e.g. taxi light, landing light or a light attached to a scenery model like a light pole) at runtime.&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-protocolkml KML Exporter (Google Earth)] - ([https://github.com/slawekmikula/flightgear-addon-protocolkml/blob/master/doc/manual.md manual])&lt;br /&gt;
* [[Landing Rate addon]] [https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=33101&amp;amp;p=327787#p327787]&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-linuxtrack LinuxTrack Head Tracker integration] - ([https://github.com/slawekmikula/flightgear-addon-linuxtrack/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-littlenavmap LittleNavMap integration] - ([https://github.com/slawekmikula/flightgear-addon-littlenavmap/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/PlayeRom/flightgear-addon-logbook Logbook] all your flights to CSV file.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?t=41070&lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Logbook Add-on &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Roman Ludwicki (PlayeRom) &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Dec 11th, 2022&lt;br /&gt;
  |added  =  Dec 11th, 2022&lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Log time-stamper add-on]] - Will print simulated UTC time, real local time, and/or real UTC time time-stamps at configurable intervals to the console and log.&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-hax mdanilov hax!]: landing evaluation and aircraft development tools, TerraSync toggler&lt;br /&gt;
* [[Model Cockpit View]] &lt;br /&gt;
* [https://github.com/SP-NTX/MPChatImprovments MPChatImprovments] Multi-key commands for Multiplayer, new features for chat,...&lt;br /&gt;
* [https://github.com/hbeni/fgfs-noGroundDamage noGroundDamage] - Addon to temporarily disable damage after landing and for ground operations for the c172/c182&lt;br /&gt;
* [[Oscilloscope addon]] - Allows displaying a property of Nasal function over time&lt;br /&gt;
* [[PAR instrument]] - Precision Approach Radar and Ground Controlled Approach&lt;br /&gt;
* [[Red Griffin ATC]] - Speaking Air Traffic Controller&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?f=6&amp;amp;t=36755 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; RedGriffin &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 6th, 2020 &lt;br /&gt;
  |added  =  Jan 6th, 2020 &lt;br /&gt;
  |script_version = 1.0.0 RC2 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [https://github.com/tdammers/fg-simbrief-addon SimBrief import] - Import flightplans, weights, fuel, and winds alof, from SimBrief.&lt;br /&gt;
* [[Spoken ATC]] (ported by Torsten)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=314095#p314095 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 10th, 2017 &lt;br /&gt;
  |added  =  Jul 10th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
* [[Spoken GCA]] - An offline ground controlled approach (GCA) addon&lt;br /&gt;
* [https://gitlab.com/mdanil/flightgear-mickey Tiny HUD for mouse flying in FlightGear] - Visual feedback for mouse flying, to make up for mouse's lack of self-centering&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrflight VFRFlight integration] - ([https://github.com/slawekmikula/flightgear-addon-vfrflight/blob/master/doc/manual.md manual])&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-vfrnavigator VFR Flying Helper] - ([https://github.com/slawekmikula/flightgear-addon-vfrnavigator/blob/master/doc/usage.md manual])&lt;br /&gt;
* [[YASim Development Tools]] (by jsb)&lt;br /&gt;
&lt;br /&gt;
== Experimental Addons ==&lt;br /&gt;
&lt;br /&gt;
Addons which are in the development stage/unfinished but can be used as a quick view of addon functionality&lt;br /&gt;
* [https://github.com/slawekmikula/flightgear-addon-missions FlightGear Missions addon] - Add-on for missions/adventures code&lt;br /&gt;
&lt;br /&gt;
== Ideas ==&lt;br /&gt;
=== Hooking into features using legacy OpenGL code ===&lt;br /&gt;
{{See also|Unifying the 2D rendering backend via canvas}}&lt;br /&gt;
In 09/2018, James suggested that legacy features using raw OpenGL calls (e.g. HUD/2D panels) could be easily replaced via scripted Canvas/Nasal solutions at the mere cost of providing a mechanism to hook into the legacy code implementing these features (namely, the HUD/instrumentation subsystems) &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/36399261/&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Catalog &amp;amp; Package Manager support ===&lt;br /&gt;
if this works with more complex, pre-existing addons such as [[Bombable]], where the file layout is a replica of the old FGData layout, these types of mature addons might be better as SourceForge FlightGear sub-projects rather than being copied into FGAddon. Maybe the config file and nasal script could be used to automate the installation process ?&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953179/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
The [[Bombable]] addon is one of the most popular addons out there, and a large number of aircraft in FGAddon have bombable support, so it is worth not forgetting about. Especially if the addon system one day becomes automated through a [[Catalog metadata|catalog.xml type system]]&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35953650/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Simple API for creating FlightGear&lt;br /&gt;
 addons/plugins &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Edward d'Auvergne &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 19th, 2017 &lt;br /&gt;
  |added  =  Jul 19th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[FG Add-on FAQ]]&lt;br /&gt;
* [[Modules.nas]]&lt;br /&gt;
* [[FlightGear configuration via XML]]&lt;br /&gt;
* [[FlightGear configuration via XML#preferences.xml]]&lt;br /&gt;
* [[Nasal]]&lt;br /&gt;
* [[Property tree]]&lt;br /&gt;
* [[Properties persistent between sessions]]&lt;br /&gt;
* [[PropertyList XML File]]&lt;br /&gt;
&lt;br /&gt;
=== Forum topics ===&lt;br /&gt;
* {{forum link|t=32561|title=New Feature: Addon - &amp;quot;API&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
=== Readme files ===&lt;br /&gt;
* {{readme file|add-ons}}&lt;br /&gt;
* {{readme file|gui}} - Details on how to add menus and custom dialogs.&lt;br /&gt;
&lt;br /&gt;
=== Source code ===&lt;br /&gt;
==== FGAddon ====&lt;br /&gt;
* {{fgaddon source|path=Addons/Skeleton}} - Skeleton addon to be used as a template.&lt;br /&gt;
&lt;br /&gt;
==== FGData ====&lt;br /&gt;
* {{fgdata source|path=Nasal/addons.nas}}&lt;br /&gt;
&lt;br /&gt;
==== FlightGear ====&lt;br /&gt;
* {{flightgear source|path=src/Main/options.cxx}}&lt;br /&gt;
* {{flightgear source|path=src/Add-ons/}}&lt;br /&gt;
&lt;br /&gt;
[[Category:FlightGear addons| ]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141676</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141676"/>
		<updated>2025-04-01T17:41:11Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''NOTE: This &amp;quot;raster calculator&amp;quot; process or method is considered suboptimal to the Python script method below. The Python script method below has newer smoothing processes. However, this method does contain useful knowledge on how to use the raster calculator. It needs to be updated to match the Python script method.'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
################################## IMPORTANT ###############################&lt;br /&gt;
# Beginning filename of the NLCD from the source needs to be&lt;br /&gt;
# NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
# Also note that &amp;quot;/data/&amp;quot; is hardcoded into the path as well&lt;br /&gt;
# I needed the existing path structure to process it by state.&lt;br /&gt;
# For Alaska I used one lat and a few lon sections per chunck built.&lt;br /&gt;
# I will likly do the entire NLCD coverage area the same in future runs.&lt;br /&gt;
# Example beginning NLCD source:&lt;br /&gt;
# NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
# Full path and filename to source example is:&lt;br /&gt;
# G:/Scenery/ws3.0/Alaska/data/NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
# Final output path to last processed file is:&lt;br /&gt;
# G:/Scenery/ws3.0/Alaska/data/NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326&lt;br /&gt;
&lt;br /&gt;
#IMPORTANT NOTE:&lt;br /&gt;
# On any subprocess.run(command, shell=True) process, when using the script in Linux&lt;br /&gt;
# you need to change to subprocess.run(command). Remove the shell=True.&lt;br /&gt;
&lt;br /&gt;
#You can try using the following syntax for both Windows and Linux&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'gdal_calc.py',&lt;br /&gt;
#    '-A', input_path,&lt;br /&gt;
#    '--outfile', output_path,&lt;br /&gt;
#    '--calc', expression,&lt;br /&gt;
#    '--type', 'Byte'&lt;br /&gt;
#], check=True)&lt;br /&gt;
############################################################################&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141538</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141538"/>
		<updated>2025-03-15T16:14:30Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''NOTE: This &amp;quot;raster calculator&amp;quot; process or method is considered suboptimal to the Python script method below. The Python script method below has newer smoothing processes. However, this method does contain useful knowledge on how to use the raster calculator. It needs to be updated to match the Python script method.'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
################################## IMPORTANT ###############################&lt;br /&gt;
# Beginning filename of the NLCD from the source needs to be&lt;br /&gt;
# NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
# Also note that &amp;quot;/data/&amp;quot; is hardcoded into the path as well&lt;br /&gt;
# I needed the existing path structure to process it by state.&lt;br /&gt;
# For Alaska I used one lat and a few lon sections per chunck built.&lt;br /&gt;
# I will likly do the entire NLCD coverage area the same in future runs.&lt;br /&gt;
# Example beginning NLCD source:&lt;br /&gt;
# NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
# Full path and filename to source example is:&lt;br /&gt;
# G:/Scenery/ws3.0/Alaska/data/NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
# Final output path to last processed file is:&lt;br /&gt;
# G:/Scenery/ws3.0/Alaska/data/NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326&lt;br /&gt;
############################################################################&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141537</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141537"/>
		<updated>2025-03-15T15:51:06Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''NOTE: This &amp;quot;raster calculator&amp;quot; process or method is considered suboptimal to the Python script method below. The Python script method below has newer smoothing processes. However, this method does contain useful knowledge on how to use the raster calculator. It needs to be updated to match the Python script method.'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141536</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141536"/>
		<updated>2025-03-15T15:50:45Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''NOTE: This process or method is considered suboptimal to the Python script method below. The Python script method below has newer smoothing processes. However, this method does contain useful knowledge on how to use the raster calculator. It needs to be updated to match the Python script method.'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141535</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141535"/>
		<updated>2025-03-15T15:50:17Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''NOTE: This process or method is considered suboptimal to the Python script method below. The Python method below has newer smoothing processes. However, this method does contain useful knowledge on how to use the raster calculator. It needs to be updated to match the Python script method.'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141534</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141534"/>
		<updated>2025-03-15T15:50:07Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
'''NOTE: This process or method is considered suboptimal to the Python script method below. The Python method below has newer smoothing processes. However, this method does contain useful knowledge on how to use the raster calculator. It needs to be updated to match the Python script method.'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141533</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141533"/>
		<updated>2025-03-15T14:59:18Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Change beginning file name to show naming strategy&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Land_Cover_Alaska141-140_60&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2016_Alaska141-140_60_Smoothed-HD-Compressed_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141532</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141532"/>
		<updated>2025-03-15T14:53:14Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Change to a more streamline script, only open the last raster in QGIS.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsApplication, QgsCoordinateTransform, QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException, QgsRasterBlock, QgsRectangle&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from qgis import processing&lt;br /&gt;
from processing.core.Processing import Processing&lt;br /&gt;
from osgeo import gdal, osr, ogr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2016';&lt;br /&gt;
state = 'Alaska';&lt;br /&gt;
part = '141-140_60';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
#FG                     NLCD&lt;br /&gt;
#23 DeciduousBroadCover 41&lt;br /&gt;
#24 EvergreenForest     42&lt;br /&gt;
#25 MixedForest         43&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Tree_Canopy_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_Land_Cover_' + state + part + '.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_path,&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,  # GDAL GDT_Byte&lt;br /&gt;
        'TARGET_EXTENT':None,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Land_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 44 + '&lt;br /&gt;
    '(A == 11) * 44 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Replace urban and clutter with grass completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Land_Combined_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--type', 'Byte'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6:  Reclass urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Reclassed-Urban_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Reclassed-Urban_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 2=mode&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
input_path_a = path + state + '/data/NLCD_' + year + '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
input_path_b = path + state + '/data/NLCD_' + year + '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year + '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
   '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
   '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_path_a,&lt;br /&gt;
    '-B', input_path_b,&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression,&lt;br /&gt;
    '--NoDataValue', '0'&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(input_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path,&lt;br /&gt;
    input_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes,&lt;br /&gt;
    outputType=gdal.GDT_Byte  # Set the output type to uint8&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_path,&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1, #1=median, 3=mode&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Convert to 8Bit #################################################################&lt;br /&gt;
&lt;br /&gt;
input_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
	&amp;quot;gdal:translate&amp;quot;,&lt;br /&gt;
	{&lt;br /&gt;
		'INPUT':input_path,&lt;br /&gt;
		'TARGET_CRS':None,&lt;br /&gt;
		'NODATA':0,&lt;br /&gt;
		'COPY_SUBDATASETS':False,&lt;br /&gt;
		'OPTIONS':'',&lt;br /&gt;
		'EXTRA':'',&lt;br /&gt;
		'DATA_TYPE':1,&lt;br /&gt;
		'OUTPUT':output_path,&lt;br /&gt;
		'OPTIONS': 'COMPRESS=LZW'&lt;br /&gt;
	}&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141404</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141404"/>
		<updated>2025-02-25T16:52:17Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
# Note: this step is going to increase the size of Combined-Clean_4326 file by 64 times (8 x 8). So it can take a long time.&lt;br /&gt;
# You can watch the file size grow to have a better idea of hold long Step 9 is going to take to finish.&lt;br /&gt;
# The Combined-Clean-HD_4326 final file will be 64 x the Combined-Clean_4326 file size.&lt;br /&gt;
# 1/4 to 1/3 of the State of California took as much as 36-48 hours to process.&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Reclass any no-data (nan) to 41 ###########################################################&lt;br /&gt;
&lt;br /&gt;
# NOTE: Step 12 compress may need tobe done here if using the --shoreline option in genVPB.py&lt;br /&gt;
# This step to convert nodata (0) to 41 or 44 was done as nodata was showing up in the final scenery as a city or urban land cover&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Corrected_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    'nan_to_num(A, nan=41)'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 12: Compress ###########################################################&lt;br /&gt;
&lt;br /&gt;
compressed_output_path = output_path.replace(&amp;quot;.tif&amp;quot;, &amp;quot;_Compressed.tif&amp;quot;)&lt;br /&gt;
command_compress = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-of', 'GTiff',&lt;br /&gt;
    '-co', 'COMPRESS=LZW',&lt;br /&gt;
    output_path,&lt;br /&gt;
    compressed_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command_compress, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(compressed_output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141403</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141403"/>
		<updated>2025-02-25T16:48:12Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
# Note: this step is going to increase the size of Combined-Clean_4326 file by 64 times (8 x 8). So it can take a long time.&lt;br /&gt;
# You can watch the file size grow to have a better idea of hold long Step 9 is going to take to finish.&lt;br /&gt;
# The Combined-Clean-HD_4326 final file will be 64 x the Combined-Clean_4326 file size.&lt;br /&gt;
# 1/4 to 1/3 of the State of California took as much as 36-48 hours to process.&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Reclass any no-data (nan) to 41 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Corrected_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    'nan_to_num(A, nan=41)'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
compressed_output_path = output_path.replace(&amp;quot;.tif&amp;quot;, &amp;quot;_Compressed.tif&amp;quot;)&lt;br /&gt;
command_compress = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-of', 'GTiff',&lt;br /&gt;
    '-co', 'COMPRESS=LZW',&lt;br /&gt;
    output_path,&lt;br /&gt;
    compressed_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command_compress, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(compressed_output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Compressed_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Compressed_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141402</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141402"/>
		<updated>2025-02-24T20:42:10Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
# Note: this step is going to increase the size of Combined-Clean_4326 file by 64 times (8 x 8). So it can take a long time.&lt;br /&gt;
# You can watch the file size grow to have a better idea of hold long Step 9 is going to take to finish.&lt;br /&gt;
# The Combined-Clean-HD_4326 final file will be 64 x the Combined-Clean_4326 file size.&lt;br /&gt;
# 1/4 to 1/3 of the State of California took as much as 36-48 hours to process.&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 11: Reclass any no-data (nan) to 41 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Corrected_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    'nan_to_num(A, nan=41)'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
compressed_output_path = output_path.replace(&amp;quot;.tif&amp;quot;, &amp;quot;_compressed.tif&amp;quot;)&lt;br /&gt;
command_compress = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-of', 'GTiff',&lt;br /&gt;
    '-co', 'COMPRESS=LZW',&lt;br /&gt;
    output_path,&lt;br /&gt;
    compressed_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command_compress, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(compressed_output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD-Corrected_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 11:  Reclass no-data (nan) to 41. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD-Corrected_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141380</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141380"/>
		<updated>2025-02-17T15:42:45Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
# Note: this step is going to increase the size of Combined-Clean_4326 file by 64 times (8 x 8). So it can take a long time.&lt;br /&gt;
# You can watch the file size grow to have a better idea of hold long Step 9 is going to take to finish.&lt;br /&gt;
# The Combined-Clean-HD_4326 final file will be 64 x the Combined-Clean_4326 file size.&lt;br /&gt;
# 1/4 to 1/3 of the State of California took as much as 36-48 hours to process.&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141379</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141379"/>
		<updated>2025-02-17T15:32:15Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141378</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141378"/>
		<updated>2025-02-17T15:32:01Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
The below python script produces more refined scenery than this method. I left this documentation though as it is a good starting point and has examples of how you can manipulate rasters using the calculator. It was the first steps I took to learn how all this stuff works. It could be updated to match all the steps used in the python script method.&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141377</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141377"/>
		<updated>2025-02-17T15:28:18Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
This python script method produces more refined data than by using the python calculator method above and it is more automated.&lt;br /&gt;
If you run this script outside the python console you will need to modify it to locate the data you are processing.&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141376</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141376"/>
		<updated>2025-02-17T15:16:13Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_Smoothed-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
The source location for the data will be something like&lt;br /&gt;
DRIVE:/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
# &amp;quot;path&amp;quot; is location &lt;br /&gt;
# NLCD typically has a &amp;quot;year&amp;quot; stamp it was taken&lt;br /&gt;
# &amp;quot;state&amp;quot; matches the folder name you data located in.&lt;br /&gt;
# You don't have to produce data by state, you can use another convention such as lat/lon, just use &amp;quot;part&amp;quot; below and leave the &amp;quot;state&amp;quot; blank&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two lat/lon&lt;br /&gt;
# Just make sure you match the above input and output location parameters to match the layout of your data.&lt;br /&gt;
# And that they match the variables they are assigned to in all the steps below&lt;br /&gt;
# Example of one of the steps below that the above paths are used to populate&lt;br /&gt;
# output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141375</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141375"/>
		<updated>2025-02-17T14:56:25Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names and paths&lt;br /&gt;
path = 'G:/Scenery/ws3.0/';&lt;br /&gt;
# NLCD typically has a year stamp it was taken&lt;br /&gt;
year = '2021';&lt;br /&gt;
# state matches the folder name you data located in&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
# part is for any special name addition you might need to use, ex. North, South, one, two&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = path + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141373</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141373"/>
		<updated>2025-02-17T06:45:55Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
combined_clean = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(combined_clean)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 9: Upsample to HD #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
# Open the original raster&lt;br /&gt;
ds = gdal.Open(output_path)&lt;br /&gt;
gt = ds.GetGeoTransform()&lt;br /&gt;
&lt;br /&gt;
# Extract the original resolution&lt;br /&gt;
original_xRes = gt[1]&lt;br /&gt;
original_yRes = abs(gt[5])&lt;br /&gt;
&lt;br /&gt;
# Define the percentage to resize by (e.g., 0.50 for 50%, 2.0 for 200%)&lt;br /&gt;
percentage = 8.0  # Adjust this as needed&lt;br /&gt;
&lt;br /&gt;
# Calculate the new resolution&lt;br /&gt;
new_xRes = original_xRes / percentage&lt;br /&gt;
new_yRes = original_yRes / percentage&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(part + &amp;quot;_Combined-Clean_4326.tiff&amp;quot;, part + &amp;quot;_Combined-Clean-HD_4326.tiff&amp;quot;)&lt;br /&gt;
# Perform the warp (resampling)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=new_xRes,&lt;br /&gt;
    yRes=new_yRes&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
hd = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(hd)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Upsample to. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 10: Smooth all features in original dataset #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean-HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
#subprocess.run([&lt;br /&gt;
#    'C:/Program Files/QGIS 3.28.2/apps/grass/grass78/bin/g.region',&lt;br /&gt;
#    'n=54.5621531092570109', 's=52.3556994132482245',&lt;br /&gt;
#    'e=-165.5354371080028386', 'w=-170.5589081482369807',&lt;br /&gt;
#    'res=0.0001'  # Replace with the native resolution of your raster&lt;br /&gt;
#])&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        #1=median, 3=mode&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':11,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
		'weight':'',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight3.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight7.txt',&lt;br /&gt;
        #'weight':'G:/Scenery/ws3.0/weight11.txt',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        #'GRASS_REGION_PARAMETER':None,&lt;br /&gt;
		#'GRASS_REGION_PARAMETER': {&lt;br /&gt;
        #    'extent': 'user',&lt;br /&gt;
        #    'north': 54.5621531092570109,&lt;br /&gt;
        #    'south': 52.3556994132482245,&lt;br /&gt;
        #    'east': -165.5354371080028386,&lt;br /&gt;
        #    'west': -170.5589081482369807,&lt;br /&gt;
        #    'resolution': 0.0001  # Set the desired resolution&lt;br /&gt;
        #},&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
) &lt;br /&gt;
&lt;br /&gt;
result_smoothed = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Smoothed-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_smoothed)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 10: Smooth all features in original dataset completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Smoothed-HD_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141372</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141372"/>
		<updated>2025-02-17T06:44:13Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(&amp;quot;_4326.tiff&amp;quot;, &amp;quot;_HD_4326.tiff&amp;quot;)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=0.00005539416,&lt;br /&gt;
    yRes=0.00005539416&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
urban_grass_combined = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(urban_grass_combined)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_HD_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################## Step 9: Smooth all hi res features #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)  &lt;br /&gt;
&lt;br /&gt;
# Convert output to Byte using gdal_translate&lt;br /&gt;
converted_output_path = output_path.replace(&amp;quot;_HD_4326.tiff&amp;quot;, &amp;quot;_Final-HD_4326.tiff&amp;quot;)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-ot', 'Byte',  # Set output data type to Byte&lt;br /&gt;
    output_path,&lt;br /&gt;
    converted_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(converted_output_path, 'NLCD_' + year +  '_' + state + part + '_Final-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Smooth all hi res features completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Final-HD_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141371</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141371"/>
		<updated>2025-02-17T06:41:54Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 0) * 41 + '&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--type=Byte',&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(&amp;quot;_4326.tiff&amp;quot;, &amp;quot;_HD_4326.tiff&amp;quot;)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=0.00005539416,&lt;br /&gt;
    yRes=0.00005539416&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
urban_grass_combined = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(urban_grass_combined)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_HD_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################## Step 9: Smooth all hi res features #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)  &lt;br /&gt;
&lt;br /&gt;
# Convert output to Byte using gdal_translate&lt;br /&gt;
converted_output_path = output_path.replace(&amp;quot;_HD_4326.tiff&amp;quot;, &amp;quot;_Final-HD_4326.tiff&amp;quot;)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-ot', 'Byte',  # Set output data type to Byte&lt;br /&gt;
    output_path,&lt;br /&gt;
    converted_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(converted_output_path, 'NLCD_' + year +  '_' + state + part + '_Final-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Smooth all hi res features completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Final-HD_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141370</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=141370"/>
		<updated>2025-02-17T06:35:44Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
Usage: genVPB.py --raster &amp;lt;input-raster&amp;gt; [ option ... ]&lt;br /&gt;
Usage: genVPB.py --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; --sentinel --reclass &amp;lt;reclass&amp;gt; [ option ... ]&lt;br /&gt;
&lt;br /&gt;
  Arguments:&lt;br /&gt;
&lt;br /&gt;
  --raster &amp;lt;input-raster&amp;gt;   - Input landclass raster&lt;br /&gt;
  --bbox &amp;lt;lat0&amp;gt; &amp;lt;lon0&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon1&amp;gt; - Bounding box of scenery to be generated.&lt;br /&gt;
  --sentinel &amp;lt;raster-dir&amp;gt;   - Using Sentinel-2 Landclass data from &amp;lt;raster-dir&amp;gt;&lt;br /&gt;
  --auto-download           - Auto-download Sentinel-2 Landclass data.  Only valid with --sentinel&lt;br /&gt;
  --hgt-dir &amp;lt;hgt-dir&amp;gt;       - Set directory containing unzipped NASADEM HGT files. Defaults to '{DEFAULT_HGT_DIR}'.&lt;br /&gt;
  --output-dir &amp;lt;output-dir&amp;gt; - Set output directory. Defaults to '{DEFAULT_OUTPUT_DIR}'.&lt;br /&gt;
  --reclass &amp;lt;reclass&amp;gt;       - Reclassify raster using file &amp;lt;reclass&amp;gt;. See fgmeta/ws30/mappings/.&lt;br /&gt;
  --coastline &amp;lt;coastline&amp;gt;   - Clip against coastline against polygon (.osm).  E.g. from https://osmdata.openstreetmap.de/data/land-polygons.html.&lt;br /&gt;
  --shrink-water &amp;lt;pixels&amp;gt;   - Shrink water bodies (landclasses 40, 41) by &amp;lt;pixels&amp;gt; pixels, typically in combination with generating a water raster.&lt;br /&gt;
  --generate-water-raster   - Generate a water raster from OSM data.&lt;br /&gt;
  --cache-dir &amp;lt;dir&amp;gt;         - Use &amp;lt;dir&amp;gt; as a cache of OSM data to improve performance and reduce load on Overpass API.&lt;br /&gt;
  --nasadem-server &amp;lt;server&amp;gt; - Set server to download NASADEM data from. Defaults to {getNASADEM.DEFAULT_SERVER}&lt;br /&gt;
  --nasadem-user &amp;lt;user&amp;gt;     - NASA Earthdata username. Required to download NASADEM automatically. To create a username, see https://urs.earthdata.nasa.gov/users/new/.&lt;br /&gt;
  --nasadem-password &amp;lt;password&amp;gt; - NASA Earthdata password. Required to download NASADEM automatically.&lt;br /&gt;
  --debug                   - Debug output, generate intermediate geoTIFFs and don't compress output.&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
&amp;lt;code&amp;gt;./scripts/genVPB.py --bbox -55 -3 56 -2 --raster ./data/uk_wgs84_10m_N54.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
&lt;br /&gt;
Usage: genwaterraster.py &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;scenery-dir&amp;gt; [cache-dir]&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;min-lat&amp;gt; &amp;lt;min-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;max-lon - bounding box of water to generated&lt;br /&gt;
  &amp;lt;scenery_dir&amp;gt;  Scenery directory to write to&lt;br /&gt;
  [Cache]        Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 &amp;lt;code&amp;gt;./scripts/genwaterraster.py 55 -4 56 -3 ./output/vpb ./cache&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the &amp;lt;code&amp;gt;Landclass&amp;lt;/code&amp;gt; column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help. You input e.g. SE and SW coordinates and calculate to get the distance in Km. Then you multiply by thousand and devide by the number of metres per pixel (e.g. 5) -&amp;gt; resolution for width.&lt;br /&gt;
* Width/Horizontal Resolution. Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution. Enter the values you've calculated for the vertical resolution (latitude or the height of the raster)&lt;br /&gt;
* Output extent - Select an option from the box on the right. You can edit the text afterwards (NB: East, West, South, North). Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
I QGIS make sure that only the layer for the raster for land data is selected (e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;) -&amp;gt; in the map view you will see the whole earth. NB: typically you do this reclassify only once after download and can reuse the result for future processing.&lt;br /&gt;
&lt;br /&gt;
Then: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save your changes (overwrite e.g. &amp;lt;code&amp;gt;land-polygons-split-4326&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (A &amp;lt; 255)) * 43 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(((B &amp;gt; 0) &amp;amp; (B &amp;lt; 255)) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--type=Byte',&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(&amp;quot;_4326.tiff&amp;quot;, &amp;quot;_HD_4326.tiff&amp;quot;)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=0.00005539416,&lt;br /&gt;
    yRes=0.00005539416&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
urban_grass_combined = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(urban_grass_combined)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_HD_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################## Step 9: Smooth all hi res features #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)  &lt;br /&gt;
&lt;br /&gt;
# Convert output to Byte using gdal_translate&lt;br /&gt;
converted_output_path = output_path.replace(&amp;quot;_HD_4326.tiff&amp;quot;, &amp;quot;_Final-HD_4326.tiff&amp;quot;)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-ot', 'Byte',  # Set output data type to Byte&lt;br /&gt;
    output_path,&lt;br /&gt;
    converted_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(converted_output_path, 'NLCD_' + year +  '_' + state + part + '_Final-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Smooth all hi res features completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Final-HD_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=File:Immatriculation_modeling_in_Blender.png&amp;diff=140819</id>
		<title>File:Immatriculation modeling in Blender.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=File:Immatriculation_modeling_in_Blender.png&amp;diff=140819"/>
		<updated>2024-10-23T17:41:55Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* {{int:filedesc}} */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{int:filedesc}}==&lt;br /&gt;
{{Information&lt;br /&gt;
|description={{en|1=Showing the immatriculation modeling in Blender that is used in on the DR400 model.}}&lt;br /&gt;
|date=2024-10-23&lt;br /&gt;
|source={{own}}&lt;br /&gt;
|author=[[User:Wlbragg|Wlbragg]]&lt;br /&gt;
|permission=&lt;br /&gt;
|other versions=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{int:license-header}}==&lt;br /&gt;
{{self|cc-by-sa-4.0}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Immatriculation modeling Blender registration]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140818</id>
		<title>Immatriculation</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140818"/>
		<updated>2024-10-23T17:40:37Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Note|The [[Canvas]] system is a more recent, and much simpler, option to add immatriculation support to an aircraft using CanvasText/osgText nodes (typically, 5 lines of code), for details see: [[Howto:Dynamic Liveries via Canvas]]}}&lt;br /&gt;
&lt;br /&gt;
[[File:DR400_immat.jpg|thumb|270px|Immatriclation at the tail of the [[Robin DR400|DR400]].]]&lt;br /&gt;
&lt;br /&gt;
'''Immatriculation''' makes it possible to add a user changeable callsign to an [[aircraft]].&lt;br /&gt;
&lt;br /&gt;
Examples are based on the [[Robin DR400]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Please note that this article does not cover the model part! Following only this page, will not result in a working immatriclation!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== -set.xml ===&lt;br /&gt;
Add this to enable the immatriculation:&lt;br /&gt;
 &amp;lt;model&amp;gt;&lt;br /&gt;
  &amp;lt;immat&amp;gt;true&amp;lt;/immat&amp;gt;&lt;br /&gt;
 &amp;lt;/model&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's make a menu button:&lt;br /&gt;
 &amp;lt;menubar&amp;gt;&lt;br /&gt;
  &amp;lt;default&amp;gt;&lt;br /&gt;
   &amp;lt;menu n=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;Robin DR400&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;enabled type=&amp;quot;bool&amp;quot;&amp;gt;true&amp;lt;/enabled&amp;gt;&lt;br /&gt;
    &amp;lt;item&amp;gt;&lt;br /&gt;
      &amp;lt;label&amp;gt;Immatriculation&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;binding&amp;gt;&lt;br /&gt;
        &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;script&amp;gt;dr400.immat_dialog.toggle()&amp;lt;/script&amp;gt;&lt;br /&gt;
      &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/item&amp;gt;&lt;br /&gt;
   &amp;lt;/menu&amp;gt;&lt;br /&gt;
  &amp;lt;/default&amp;gt;&lt;br /&gt;
 &amp;lt;/menubar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;/sim&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And place a path to the immat.nas file we will create in the next step.&lt;br /&gt;
 &amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;file&amp;gt;Aircraft/DR400/Nasal/immat.nas&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== immat.nas ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ===========================&lt;br /&gt;
# Immatriculation by Zakharov&lt;br /&gt;
# ===========================&lt;br /&gt;
&lt;br /&gt;
var refresh_immat = func {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1).getValue();&lt;br /&gt;
    var immat_size = size(immat);&lt;br /&gt;
    if (immat_size != 0) immat = string.uc(immat);&lt;br /&gt;
    for (var i = 0; i &amp;lt; 6; i += 1) {&lt;br /&gt;
	if (i &amp;gt;= immat_size)&lt;br /&gt;
	    glyph = -1;&lt;br /&gt;
	elsif (string.isupper(immat[i]))&lt;br /&gt;
		glyph = immat[i] - `A`;&lt;br /&gt;
	elsif (string.isdigit(immat[i]))&lt;br /&gt;
	    glyph = immat[i] - `0` + 26;&lt;br /&gt;
	else&lt;br /&gt;
	   glyph = 36;&lt;br /&gt;
	props.globals.getNode(&amp;quot;/sim/multiplay/generic/int[&amp;quot;~i~&amp;quot;]&amp;quot;, 1).setValue(glyph+1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var immat_dialog = gui.Dialog.new(&amp;quot;/sim/gui/dialogs/DR400/status/dialog&amp;quot;,&lt;br /&gt;
				  &amp;quot;Aircraft/DR400/Dialogs/immat.xml&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
setlistener(&amp;quot;/sim/signals/fdm-initialized&amp;quot;, func {&lt;br /&gt;
  if (props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;) == nil) {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1);&lt;br /&gt;
    var callsign = props.globals.getNode(&amp;quot;/sim/multiplay/callsign&amp;quot;).getValue();&lt;br /&gt;
    if (callsign != &amp;quot;callsign&amp;quot;) immat.setValue(callsign);&lt;br /&gt;
  else immat.setValue(&amp;quot;F-GHYQ&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  refresh_immat();&lt;br /&gt;
  setlistener(&amp;quot;sim/model/immat&amp;quot;, refresh_immat, 0);&lt;br /&gt;
},0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Example of the model part mentioned above!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
There are four areas of the aircraft registration that are covered in this file, the Wing, left and right fuselage, and interior panel.&lt;br /&gt;
The list of model parts on the left side of the image are the individual letter or number positions of the final registration identification string.&lt;br /&gt;
[[File:Immatriculation_modeling_in_Blender.png|thumb|640px|Immatriculation Blender Model For the DR400.]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140817</id>
		<title>Immatriculation</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140817"/>
		<updated>2024-10-23T17:39:41Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Note|The [[Canvas]] system is a more recent, and much simpler, option to add immatriculation support to an aircraft using CanvasText/osgText nodes (typically, 5 lines of code), for details see: [[Howto:Dynamic Liveries via Canvas]]}}&lt;br /&gt;
&lt;br /&gt;
[[File:DR400_immat.jpg|thumb|270px|Immatriclation at the tail of the [[Robin DR400|DR400]].]]&lt;br /&gt;
&lt;br /&gt;
'''Immatriculation''' makes it possible to add a user changeable callsign to an [[aircraft]].&lt;br /&gt;
&lt;br /&gt;
Examples are based on the [[Robin DR400]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Please note that this article does not cover the model part! Following only this page, will not result in a working immatriclation!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== -set.xml ===&lt;br /&gt;
Add this to enable the immatriculation:&lt;br /&gt;
 &amp;lt;model&amp;gt;&lt;br /&gt;
  &amp;lt;immat&amp;gt;true&amp;lt;/immat&amp;gt;&lt;br /&gt;
 &amp;lt;/model&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's make a menu button:&lt;br /&gt;
 &amp;lt;menubar&amp;gt;&lt;br /&gt;
  &amp;lt;default&amp;gt;&lt;br /&gt;
   &amp;lt;menu n=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;Robin DR400&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;enabled type=&amp;quot;bool&amp;quot;&amp;gt;true&amp;lt;/enabled&amp;gt;&lt;br /&gt;
    &amp;lt;item&amp;gt;&lt;br /&gt;
      &amp;lt;label&amp;gt;Immatriculation&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;binding&amp;gt;&lt;br /&gt;
        &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;script&amp;gt;dr400.immat_dialog.toggle()&amp;lt;/script&amp;gt;&lt;br /&gt;
      &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/item&amp;gt;&lt;br /&gt;
   &amp;lt;/menu&amp;gt;&lt;br /&gt;
  &amp;lt;/default&amp;gt;&lt;br /&gt;
 &amp;lt;/menubar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;/sim&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And place a path to the immat.nas file we will create in the next step.&lt;br /&gt;
 &amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;file&amp;gt;Aircraft/DR400/Nasal/immat.nas&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== immat.nas ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ===========================&lt;br /&gt;
# Immatriculation by Zakharov&lt;br /&gt;
# ===========================&lt;br /&gt;
&lt;br /&gt;
var refresh_immat = func {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1).getValue();&lt;br /&gt;
    var immat_size = size(immat);&lt;br /&gt;
    if (immat_size != 0) immat = string.uc(immat);&lt;br /&gt;
    for (var i = 0; i &amp;lt; 6; i += 1) {&lt;br /&gt;
	if (i &amp;gt;= immat_size)&lt;br /&gt;
	    glyph = -1;&lt;br /&gt;
	elsif (string.isupper(immat[i]))&lt;br /&gt;
		glyph = immat[i] - `A`;&lt;br /&gt;
	elsif (string.isdigit(immat[i]))&lt;br /&gt;
	    glyph = immat[i] - `0` + 26;&lt;br /&gt;
	else&lt;br /&gt;
	   glyph = 36;&lt;br /&gt;
	props.globals.getNode(&amp;quot;/sim/multiplay/generic/int[&amp;quot;~i~&amp;quot;]&amp;quot;, 1).setValue(glyph+1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var immat_dialog = gui.Dialog.new(&amp;quot;/sim/gui/dialogs/DR400/status/dialog&amp;quot;,&lt;br /&gt;
				  &amp;quot;Aircraft/DR400/Dialogs/immat.xml&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
setlistener(&amp;quot;/sim/signals/fdm-initialized&amp;quot;, func {&lt;br /&gt;
  if (props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;) == nil) {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1);&lt;br /&gt;
    var callsign = props.globals.getNode(&amp;quot;/sim/multiplay/callsign&amp;quot;).getValue();&lt;br /&gt;
    if (callsign != &amp;quot;callsign&amp;quot;) immat.setValue(callsign);&lt;br /&gt;
  else immat.setValue(&amp;quot;F-GHYQ&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  refresh_immat();&lt;br /&gt;
  setlistener(&amp;quot;sim/model/immat&amp;quot;, refresh_immat, 0);&lt;br /&gt;
},0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Example of the model part mentioned above!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
There are four areas of the aircraft registration that are covered in this file, the Wing, left and right fuselage, and interior panel.&lt;br /&gt;
The list of model parts on the left side of the image are the individual letter or number positions of the final registration identification string.&lt;br /&gt;
[[File:Immatriculation_modeling_in_Blender.png|thumb|640px|Immatriculation Blender Model For the c172p.]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140816</id>
		<title>Immatriculation</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140816"/>
		<updated>2024-10-23T17:38:47Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Note|The [[Canvas]] system is a more recent, and much simpler, option to add immatriculation support to an aircraft using CanvasText/osgText nodes (typically, 5 lines of code), for details see: [[Howto:Dynamic Liveries via Canvas]]}}&lt;br /&gt;
&lt;br /&gt;
[[File:DR400_immat.jpg|thumb|270px|Immatriclation at the tail of the [[Robin DR400|DR400]].]]&lt;br /&gt;
&lt;br /&gt;
'''Immatriculation''' makes it possible to add a user changeable callsign to an [[aircraft]].&lt;br /&gt;
&lt;br /&gt;
Examples are based on the [[Robin DR400]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Please note that this article does not cover the model part! Following only this page, will not result in a working immatriclation!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== -set.xml ===&lt;br /&gt;
Add this to enable the immatriculation:&lt;br /&gt;
 &amp;lt;model&amp;gt;&lt;br /&gt;
  &amp;lt;immat&amp;gt;true&amp;lt;/immat&amp;gt;&lt;br /&gt;
 &amp;lt;/model&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's make a menu button:&lt;br /&gt;
 &amp;lt;menubar&amp;gt;&lt;br /&gt;
  &amp;lt;default&amp;gt;&lt;br /&gt;
   &amp;lt;menu n=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;Robin DR400&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;enabled type=&amp;quot;bool&amp;quot;&amp;gt;true&amp;lt;/enabled&amp;gt;&lt;br /&gt;
    &amp;lt;item&amp;gt;&lt;br /&gt;
      &amp;lt;label&amp;gt;Immatriculation&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;binding&amp;gt;&lt;br /&gt;
        &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;script&amp;gt;dr400.immat_dialog.toggle()&amp;lt;/script&amp;gt;&lt;br /&gt;
      &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/item&amp;gt;&lt;br /&gt;
   &amp;lt;/menu&amp;gt;&lt;br /&gt;
  &amp;lt;/default&amp;gt;&lt;br /&gt;
 &amp;lt;/menubar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;/sim&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And place a path to the immat.nas file we will create in the next step.&lt;br /&gt;
 &amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;file&amp;gt;Aircraft/DR400/Nasal/immat.nas&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== immat.nas ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ===========================&lt;br /&gt;
# Immatriculation by Zakharov&lt;br /&gt;
# ===========================&lt;br /&gt;
&lt;br /&gt;
var refresh_immat = func {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1).getValue();&lt;br /&gt;
    var immat_size = size(immat);&lt;br /&gt;
    if (immat_size != 0) immat = string.uc(immat);&lt;br /&gt;
    for (var i = 0; i &amp;lt; 6; i += 1) {&lt;br /&gt;
	if (i &amp;gt;= immat_size)&lt;br /&gt;
	    glyph = -1;&lt;br /&gt;
	elsif (string.isupper(immat[i]))&lt;br /&gt;
		glyph = immat[i] - `A`;&lt;br /&gt;
	elsif (string.isdigit(immat[i]))&lt;br /&gt;
	    glyph = immat[i] - `0` + 26;&lt;br /&gt;
	else&lt;br /&gt;
	   glyph = 36;&lt;br /&gt;
	props.globals.getNode(&amp;quot;/sim/multiplay/generic/int[&amp;quot;~i~&amp;quot;]&amp;quot;, 1).setValue(glyph+1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var immat_dialog = gui.Dialog.new(&amp;quot;/sim/gui/dialogs/DR400/status/dialog&amp;quot;,&lt;br /&gt;
				  &amp;quot;Aircraft/DR400/Dialogs/immat.xml&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
setlistener(&amp;quot;/sim/signals/fdm-initialized&amp;quot;, func {&lt;br /&gt;
  if (props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;) == nil) {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1);&lt;br /&gt;
    var callsign = props.globals.getNode(&amp;quot;/sim/multiplay/callsign&amp;quot;).getValue();&lt;br /&gt;
    if (callsign != &amp;quot;callsign&amp;quot;) immat.setValue(callsign);&lt;br /&gt;
  else immat.setValue(&amp;quot;F-GHYQ&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  refresh_immat();&lt;br /&gt;
  setlistener(&amp;quot;sim/model/immat&amp;quot;, refresh_immat, 0);&lt;br /&gt;
},0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Example of the model part mentioned above!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
There are four areas of the aircraft registration that are covered in this file, the Wing, left and right fuselage, and interior panel.&lt;br /&gt;
The list of model parts on the left side of the image are the individual letter or number positions of the final registration identification string.&lt;br /&gt;
[[File:Immatriculation_modeling_in_Blender.png|thumb|640px|Immatriculation Blender Model For the DR400.]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=File:Immatriculation_modeling_in_Blender.png&amp;diff=140815</id>
		<title>File:Immatriculation modeling in Blender.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=File:Immatriculation_modeling_in_Blender.png&amp;diff=140815"/>
		<updated>2024-10-23T17:36:17Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Wlbragg uploaded a new version of File:Immatriculation modeling in Blender.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{int:filedesc}}==&lt;br /&gt;
{{Information&lt;br /&gt;
|description={{en|1=Showing the immatriculation modeling in Blender that is used in on the c172p model.}}&lt;br /&gt;
|date=2024-10-23&lt;br /&gt;
|source={{own}}&lt;br /&gt;
|author=[[User:Wlbragg|Wlbragg]]&lt;br /&gt;
|permission=&lt;br /&gt;
|other versions=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{int:license-header}}==&lt;br /&gt;
{{self|cc-by-sa-4.0}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Immatriculation modeling Blender registration]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140814</id>
		<title>Immatriculation</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Immatriculation&amp;diff=140814"/>
		<updated>2024-10-23T16:21:40Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Note|The [[Canvas]] system is a more recent, and much simpler, option to add immatriculation support to an aircraft using CanvasText/osgText nodes (typically, 5 lines of code), for details see: [[Howto:Dynamic Liveries via Canvas]]}}&lt;br /&gt;
&lt;br /&gt;
[[File:DR400_immat.jpg|thumb|270px|Immatriclation at the tail of the [[Robin DR400|DR400]].]]&lt;br /&gt;
&lt;br /&gt;
'''Immatriculation''' makes it possible to add a user changeable callsign to an [[aircraft]].&lt;br /&gt;
&lt;br /&gt;
Examples are based on the [[Robin DR400]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Please note that this article does not cover the model part! Following only this page, will not result in a working immatriclation!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== -set.xml ===&lt;br /&gt;
Add this to enable the immatriculation:&lt;br /&gt;
 &amp;lt;model&amp;gt;&lt;br /&gt;
  &amp;lt;immat&amp;gt;true&amp;lt;/immat&amp;gt;&lt;br /&gt;
 &amp;lt;/model&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's make a menu button:&lt;br /&gt;
 &amp;lt;menubar&amp;gt;&lt;br /&gt;
  &amp;lt;default&amp;gt;&lt;br /&gt;
   &amp;lt;menu n=&amp;quot;100&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;Robin DR400&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;enabled type=&amp;quot;bool&amp;quot;&amp;gt;true&amp;lt;/enabled&amp;gt;&lt;br /&gt;
    &amp;lt;item&amp;gt;&lt;br /&gt;
      &amp;lt;label&amp;gt;Immatriculation&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;binding&amp;gt;&lt;br /&gt;
        &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;script&amp;gt;dr400.immat_dialog.toggle()&amp;lt;/script&amp;gt;&lt;br /&gt;
      &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/item&amp;gt;&lt;br /&gt;
   &amp;lt;/menu&amp;gt;&lt;br /&gt;
  &amp;lt;/default&amp;gt;&lt;br /&gt;
 &amp;lt;/menubar&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;/sim&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And place a path to the immat.nas file we will create in the next step.&lt;br /&gt;
 &amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;file&amp;gt;Aircraft/DR400/Nasal/immat.nas&amp;lt;/file&amp;gt;&lt;br /&gt;
 &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== immat.nas ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ===========================&lt;br /&gt;
# Immatriculation by Zakharov&lt;br /&gt;
# ===========================&lt;br /&gt;
&lt;br /&gt;
var refresh_immat = func {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1).getValue();&lt;br /&gt;
    var immat_size = size(immat);&lt;br /&gt;
    if (immat_size != 0) immat = string.uc(immat);&lt;br /&gt;
    for (var i = 0; i &amp;lt; 6; i += 1) {&lt;br /&gt;
	if (i &amp;gt;= immat_size)&lt;br /&gt;
	    glyph = -1;&lt;br /&gt;
	elsif (string.isupper(immat[i]))&lt;br /&gt;
		glyph = immat[i] - `A`;&lt;br /&gt;
	elsif (string.isdigit(immat[i]))&lt;br /&gt;
	    glyph = immat[i] - `0` + 26;&lt;br /&gt;
	else&lt;br /&gt;
	   glyph = 36;&lt;br /&gt;
	props.globals.getNode(&amp;quot;/sim/multiplay/generic/int[&amp;quot;~i~&amp;quot;]&amp;quot;, 1).setValue(glyph+1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var immat_dialog = gui.Dialog.new(&amp;quot;/sim/gui/dialogs/DR400/status/dialog&amp;quot;,&lt;br /&gt;
				  &amp;quot;Aircraft/DR400/Dialogs/immat.xml&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
setlistener(&amp;quot;/sim/signals/fdm-initialized&amp;quot;, func {&lt;br /&gt;
  if (props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;) == nil) {&lt;br /&gt;
    var immat = props.globals.getNode(&amp;quot;/sim/model/immat&amp;quot;,1);&lt;br /&gt;
    var callsign = props.globals.getNode(&amp;quot;/sim/multiplay/callsign&amp;quot;).getValue();&lt;br /&gt;
    if (callsign != &amp;quot;callsign&amp;quot;) immat.setValue(callsign);&lt;br /&gt;
  else immat.setValue(&amp;quot;F-GHYQ&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  refresh_immat();&lt;br /&gt;
  setlistener(&amp;quot;sim/model/immat&amp;quot;, refresh_immat, 0);&lt;br /&gt;
},0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;'''Example of the model part mentioned above!'''&amp;lt;/font&amp;gt;&lt;br /&gt;
There are four areas of the aircraft registration that are covered in this file, the Wing, left and right fuselage, and interior panel.&lt;br /&gt;
The list of model parts on the left side of the image are the individual letter or number positions of the final registration identification string.&lt;br /&gt;
[[File:Immatriculation_modeling_in_Blender.png|thumb|640px|Immatriculation Blender Model For the c172P.]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Aircraft enhancement]]&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=File:Immatriculation_modeling_in_Blender.png&amp;diff=140813</id>
		<title>File:Immatriculation modeling in Blender.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=File:Immatriculation_modeling_in_Blender.png&amp;diff=140813"/>
		<updated>2024-10-23T15:54:52Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: Uploaded own work with UploadWizard&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{int:filedesc}}==&lt;br /&gt;
{{Information&lt;br /&gt;
|description={{en|1=Showing the immatriculation modeling in Blender that is used in on the c172p model.}}&lt;br /&gt;
|date=2024-10-23&lt;br /&gt;
|source={{own}}&lt;br /&gt;
|author=[[User:Wlbragg|Wlbragg]]&lt;br /&gt;
|permission=&lt;br /&gt;
|other versions=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{int:license-header}}==&lt;br /&gt;
{{self|cc-by-sa-4.0}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Immatriculation modeling Blender registration]]&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=140170</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=140170"/>
		<updated>2024-08-01T03:16:03Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Optional Python script to process NLCD for the USA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
 Usage: genVPB.py &amp;lt;min-lon&amp;gt; &amp;lt;min-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;input-raster&amp;gt; [--reclass &amp;lt;reclass&amp;gt;] [--coastline &amp;lt;coastline&amp;gt;] [--hgt-dir &amp;lt;hgt-dir&amp;gt;] [--output-dir &amp;lt;output-dir&amp;gt;]&lt;br /&gt;
   &amp;lt;min-lon&amp;gt; &amp;lt;min-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;max-lat&amp;gt; - bounding box of scenery to generated&lt;br /&gt;
   &amp;lt;input-raster&amp;gt;  - Input landclass raster&lt;br /&gt;
   [coastline]     - Optional coastline polygon data (.osm) to clip against.  E.g. from &amp;lt;nowiki&amp;gt;https://osmdata.openstreetmap.de/data/land-polygons.html&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   [reclass]       - Optional file containing a set of rules for reclassifying the raster similar to r.reclass. See fgmeta/ws30/mappings/&lt;br /&gt;
   [hgt-dir]       - Optional directory containing unzipped NASADEM HGT files. Defaults to '/home/flightgear/data'.&lt;br /&gt;
   [output-dir]    - Optional directory for output scenery. Defaults to '/home/flightgear/output/vpb'&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
 ./scripts/genVPB.py -3 55 -2 56 ./data/uk_wgs84_10m_N54.tif &lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
 Usage: ./scripts/genwaterraster.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genwaterraster.py ./output/vpb -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the Landclass column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help.&lt;br /&gt;
* Width/Horizontal Resolution.  Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution.    Enter the values you've calculated for the vertical resolution (latitude or the height of the raster&lt;br /&gt;
* Output extent - Select an option from the box on the right.    You can edit the text afterwards.  Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
To do this: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
=== Optional Python script to process NLCD for the USA ===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A &amp;gt; 0) * 41 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((B &amp;gt; 0) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--type=Byte',&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(&amp;quot;_4326.tiff&amp;quot;, &amp;quot;_HD_4326.tiff&amp;quot;)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=0.00005539416,&lt;br /&gt;
    yRes=0.00005539416&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
urban_grass_combined = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(urban_grass_combined)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_HD_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################## Step 9: Smooth all hi res features #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)  &lt;br /&gt;
&lt;br /&gt;
# Convert output to Byte using gdal_translate&lt;br /&gt;
converted_output_path = output_path.replace(&amp;quot;_HD_4326.tiff&amp;quot;, &amp;quot;_Final-HD_4326.tiff&amp;quot;)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-ot', 'Byte',  # Set output data type to Byte&lt;br /&gt;
    output_path,&lt;br /&gt;
    converted_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(converted_output_path, 'NLCD_' + year +  '_' + state + part + '_Final-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Smooth all hi res features completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Final-HD_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=140169</id>
		<title>Howto:Create WS3.0 terrain</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Create_WS3.0_terrain&amp;diff=140169"/>
		<updated>2024-08-01T03:14:34Z</updated>

		<summary type="html">&lt;p&gt;Wlbragg: /* Using the Python Console */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WS30 Navbar}}&lt;br /&gt;
This article provides instructions on how to generate basic WS3.0 terrain.&lt;br /&gt;
&lt;br /&gt;
WS3.0 terrain consists of three parts:&lt;br /&gt;
&lt;br /&gt;
# A terrain mesh consisting of a landclass texture draped over an elevation model.  &lt;br /&gt;
# A high resolution water raster used to show water features such as rivers, lakes and coastline with more definition&lt;br /&gt;
# Line features such as roads and railways.&lt;br /&gt;
&lt;br /&gt;
The terrain is generated by a set of tools that are packaged in a docker image for convenience.[[File:Diagram-export-21-12-2023-16 29 37.png|thumb|Basic WS3.0 Scenery Generation Process]]&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
&lt;br /&gt;
=== Set up a Workspace ===&lt;br /&gt;
Create a directory with the following sub-directories:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/vpb&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;output/Terrain&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docker ===&lt;br /&gt;
&lt;br /&gt;
# Install [https://docs.docker.com/get-started/ Docker] on your platform.&lt;br /&gt;
#Pull the docker image by running the following command&lt;br /&gt;
&lt;br /&gt;
 docker pull flightgear/ws30-vpb-generator:latest&lt;br /&gt;
Optionally, if you are hitting rate limits:&lt;br /&gt;
#Create an account on https://hub.docker.com/.  (Note that you will need to click on an email verification link before you can log in for the first time)&lt;br /&gt;
#Run &amp;lt;code&amp;gt;docker login&amp;lt;/code&amp;gt; before the '''docker pull''' command above&lt;br /&gt;
&lt;br /&gt;
== Getting the base data ==&lt;br /&gt;
You need two pieces of data for the area of scenery you are generating:&lt;br /&gt;
&lt;br /&gt;
# An elevation model (aka DEM).  This indicates what altitude each point of the surface is.&lt;br /&gt;
# Landclass data showing what type of terrain is at each point of the surface.  This is often either a Raster (effectively a texture), or vector data.  &lt;br /&gt;
&lt;br /&gt;
=== Elevation Model ===&lt;br /&gt;
Download the NASADEM elevation model for the area of scenery you wish to generate.  This is available in 1x1 degree blocks from [https://lpdaac.usgs.gov/products/nasadem_hgtv001/ here], and with an interactive browser [https://search.earthdata.nasa.gov/search here].  &lt;br /&gt;
&lt;br /&gt;
Unzip the files into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=== Landclass Raster ===&lt;br /&gt;
Download an landclass raster for the area of scenery you wish to generate.&lt;br /&gt;
&lt;br /&gt;
* For Europe, use of [https://land.copernicus.eu/pan-european/corine-land-cover/clc2018 CORINE] is recommended.&lt;br /&gt;
* For the USA [https://www.mrlc.gov/viewer/ NLCD] is recommended&lt;br /&gt;
* Sentinel-2 data is available for the entire world via [https://livingatlas.arcgis.com/landcoverexplorer/ ESRI], but has limited set of landclasses.&lt;br /&gt;
&lt;br /&gt;
Put these into the &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
More detailed terrain can be created by modifying the landclass raster, and/or generating a new raster from vector data.  These processes are discussed below.&lt;br /&gt;
&lt;br /&gt;
== Generating Terrain ==&lt;br /&gt;
To generate terrain you need to run the tools within the docker container we installed above.  The docker image is like a small, independent virtual computing environment running within your system.  This particular docker image has all the scenery generation tools already installed.&lt;br /&gt;
&lt;br /&gt;
=== Running the docker container ===&lt;br /&gt;
Firstly, get the container running from the directory containing your &amp;lt;code&amp;gt;cache&amp;lt;/code&amp;gt;,  &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; directories:&lt;br /&gt;
 docker run --rm --mount &amp;quot;type=bind,source=`pwd`/data,target=/home/flightgear/data,readonly&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/output,target=/home/flightgear/output&amp;quot; --mount &amp;quot;type=bind,source=`pwd`/cache,target=/home/flightgear/cache&amp;quot; -it flightgear/ws30-vpb-generator:latest /bin/bash&lt;br /&gt;
&lt;br /&gt;
You should now find yourself in a bash shell within your container.  You should see data and output directories which are linked to the directories you created earlier:&lt;br /&gt;
 flightgear@ddcac77f7d5e:~$ ls&lt;br /&gt;
 cache data output bin scripts&lt;br /&gt;
&lt;br /&gt;
=== Building the terrain ===&lt;br /&gt;
To build the terrain mesh, use the &amp;lt;code&amp;gt;genVPB.py&amp;lt;/code&amp;gt; tool from inside the docker container.&lt;br /&gt;
 Usage: genVPB.py &amp;lt;min-lon&amp;gt; &amp;lt;min-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;max-lat&amp;gt; &amp;lt;input-raster&amp;gt; [--reclass &amp;lt;reclass&amp;gt;] [--coastline &amp;lt;coastline&amp;gt;] [--hgt-dir &amp;lt;hgt-dir&amp;gt;] [--output-dir &amp;lt;output-dir&amp;gt;]&lt;br /&gt;
   &amp;lt;min-lon&amp;gt; &amp;lt;min-lat&amp;gt; &amp;lt;max-lon&amp;gt; &amp;lt;max-lat&amp;gt; - bounding box of scenery to generated&lt;br /&gt;
   &amp;lt;input-raster&amp;gt;  - Input landclass raster&lt;br /&gt;
   [coastline]     - Optional coastline polygon data (.osm) to clip against.  E.g. from &amp;lt;nowiki&amp;gt;https://osmdata.openstreetmap.de/data/land-polygons.html&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   [reclass]       - Optional file containing a set of rules for reclassifying the raster similar to r.reclass. See fgmeta/ws30/mappings/&lt;br /&gt;
   [hgt-dir]       - Optional directory containing unzipped NASADEM HGT files. Defaults to '/home/flightgear/data'.&lt;br /&gt;
   [output-dir]    - Optional directory for output scenery. Defaults to '/home/flightgear/output/vpb'&lt;br /&gt;
&lt;br /&gt;
For example, to generate a piece of terrain around Edinburgh (latitude 55.5, longitude 3 degrees West)&lt;br /&gt;
 ./scripts/genVPB.py -3 55 -2 56 ./data/uk_wgs84_10m_N54.tif &lt;br /&gt;
If you are using anything other than a CORINE raster you will need to reclassify the data to match the landclasses used by FlightGear.  Those classes are defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].  You can reclassify them using the files in the [https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/ scripts/mappings] directory. E.g. to reclassify NLCD2019 data you can use &amp;lt;code&amp;gt;--reclassify ./scripts/mappings/nlcd2019.txt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
genVPB.py will output the data to the output/vpb directory, in which you should find a series of files and directories.&lt;br /&gt;
&lt;br /&gt;
=== Adding water ===&lt;br /&gt;
The terrain mesh does not have highly detailed water features - as typically the source data has a resolution of 10-25m.  Water features are generated from OpenStreetMap data.  To generate water features simply run the genwaterraster.py command.&lt;br /&gt;
 Usage: ./scripts/genwaterraster.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
The scenery directory should be your output/vpb directory created above.  For example, to generate water for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genwaterraster.py ./output/vpb -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/vpb directory there should be a set of directories and .png files.&lt;br /&gt;
&lt;br /&gt;
=== Adding roads and railways ===&lt;br /&gt;
The terrain mesh does not have any line features - things like roads.  These are generated separately from OpenStreetMap data.  To generate line features simply run the genroads.py command:&lt;br /&gt;
 Usage: ./scripts/genroads.py &amp;lt;scenery_dir&amp;gt; &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt; &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt; [Cache]&lt;br /&gt;
   &amp;lt;scenery_dir&amp;gt;      Scenery directory to write to&lt;br /&gt;
   &amp;lt;lon1&amp;gt; &amp;lt;lat1&amp;gt;      Bottom left lon/lat of bounding box&lt;br /&gt;
   &amp;lt;lon2&amp;gt; &amp;lt;lat2&amp;gt;      Top right lon/lat of bounding box&lt;br /&gt;
   [Cache]            Optional directory to cache OpenStreetMap data to reduce API requests&lt;br /&gt;
&lt;br /&gt;
The scenery directory should be your output/Terrain directory created above.  For example, to generate roads for the area around Edinburgh:&lt;br /&gt;
 ./scripts/genroads.py ./output/Terrain -4 55 -3 56 ./cache&lt;br /&gt;
Inside the ./output/Terrain directory there should be a set of directories and, .STG files text files.&lt;br /&gt;
&lt;br /&gt;
==Running FlightGear with the new WS3.0 Terrain==&lt;br /&gt;
To test the new terrain, simply include the output directory in your scenery path and run FlightGear with the &amp;lt;code&amp;gt;--prop:/scenery/use-vpb=true&amp;lt;/code&amp;gt; to enable WS3.0.&lt;br /&gt;
&lt;br /&gt;
== Advanced Techniques ==&lt;br /&gt;
The following sections describe more complex techniques to generate higher quality WS3.0 terrain.  Almost all of them involve using different data sources to generate a more detailed landclass raster before running the final scenery generation processes described above.  Generating a highly detailed landclass raster is where the magic happens.  &lt;br /&gt;
&lt;br /&gt;
Most techniques use gdal or grass to modify the raster/vector data, typically using the QGIS program.&lt;br /&gt;
&lt;br /&gt;
=== Using a different elevation model ===&lt;br /&gt;
If you are using another elevation model other than NASAEM, then you may need to re-project it using QGIS/gdalwarp to the WGS84 CRS (aka EPSG:4326).  &lt;br /&gt;
&lt;br /&gt;
=== Landclass Data Requirements ===&lt;br /&gt;
For any landclass data we need to ensure the data is in the correct format.  That means:&lt;br /&gt;
&lt;br /&gt;
# Is a Raster (geotiff) rather than Vector data.  This raster will become the texture on the terrain that the terrain shaders do their magic on.&lt;br /&gt;
# Uses the WGS84 Coordinate Reference System.  The ensures that the terrain generation step is efficient.&lt;br /&gt;
# Has the correct landclass values for each terrain type.  We use a set of values based on the CORINE raster set, defined in [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml].&lt;br /&gt;
&lt;br /&gt;
Below is a quick table showing what steps you need to take for common landclass data sources.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Landclass Data&lt;br /&gt;
!Warp to WGS84 required?&lt;br /&gt;
!Landclass re-classification Required?&lt;br /&gt;
!Raster Simplification Required?&lt;br /&gt;
!Conversion to Raster Required?&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Raster&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|CORINE Vector&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|-&lt;br /&gt;
|NLCD&lt;br /&gt;
|No&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|-&lt;br /&gt;
|Sentinel-2&lt;br /&gt;
|Yes&lt;br /&gt;
|Yes&lt;br /&gt;
|No&lt;br /&gt;
|No&lt;br /&gt;
|}&lt;br /&gt;
Conversion to Raster must be done manually.  Converting to WGS84 and the correct landclasses ''can'' be done by the genVPB.py script, but slows down scenery generation.  Therefore if you are planning to generate scenery multiple times it is best to pre-process the files yourself.&lt;br /&gt;
&lt;br /&gt;
The easiest way to do these operations is using QGIS, which is available for most platforms.  If you are scripting a toolchain, the QGIS tools include command-line equivalents for all commands.&lt;br /&gt;
&lt;br /&gt;
When using QGIS, set the Project CRS to WGS84 (aka EPSG:4326).  You can then add layers of Raster or Vector data from files from the &amp;lt;code&amp;gt;Layer-&amp;gt;Add Layer&amp;lt;/code&amp;gt; menu.  When performing any operations, &amp;lt;u&amp;gt;always&amp;lt;/u&amp;gt; write out the data to a real file so you can go back to it later. Disk space is cheap :).&lt;br /&gt;
&lt;br /&gt;
=== Warping Raster Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.&lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a raster layer to a different CRS using the Raster-&amp;gt;Projections-Warp (Reproject) tool.  &lt;br /&gt;
&lt;br /&gt;
Select the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Resampling Method to Use - Nearest Neighbour.  (Landclass data is not like normal images.  You don't want to interpolate between values.)&lt;br /&gt;
* Nodata value for output bands - 0.0  (This means that any data at the edges will be Ocean, usually a reasonable default)&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
Alternatively you can do this step from the commandline.&lt;br /&gt;
 gdalwarp -t_srs EPSG:4326 -dstnodata 0.0 -r near -ot Byte -of GTiff -co COMPRESS=NONE -co BIGTIFF=IF_NEEDED /home/stuart/FlightGear/VPB/data/CORINE/u2018_clc2018_v2020_20u1_raster100m/DATA/U2018_CLC2018_V2020_20u1.tif /home/stuart/FlightGear/VPB/data/scratch/corine_WGS84.tif&lt;br /&gt;
&lt;br /&gt;
=== Warping Vector Layers to WGS84 ===&lt;br /&gt;
genVPB.py will automatically convert to WGS84 if it detects a landclass raster with a different Coordinate System.  However, you may wish to do so manually instead so all your data is on the same coordinate system.  &lt;br /&gt;
&lt;br /&gt;
In QGIS you can warp a vector layer using Vector-&amp;gt;Data Management Tools-&amp;gt;Reproject Layer.  &lt;br /&gt;
&lt;br /&gt;
Set the following options in the dialog:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - Check you have selected the correct layer. The CRS is shown at the right.&lt;br /&gt;
* Target CRS -   set to &amp;lt;code&amp;gt;EPSG:4326 - WGS84&amp;lt;/code&amp;gt;, which should be your project CRS.&lt;br /&gt;
* Reprojected - Choose a target filename&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying Vector Layers ===&lt;br /&gt;
For CORINE vector data in particular, the attributes used in the vector data are not the same as those used by the CORINE Raster data.  So we need to create a new attribute on the data.&lt;br /&gt;
[[File:Field Calculator.png|thumb|QGIS Field Calculator]]&lt;br /&gt;
To do this &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;.  You should see a table with multiple columns.  Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialog&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;quot;Whole Number (Integer)&amp;quot;.  This will create a new column which we will populate with the correct landclass data&lt;br /&gt;
* Click on the &amp;lt;code&amp;gt;Open Field Calculator&amp;lt;/code&amp;gt; button (Ctrl + I).  (If you get an error about only being able to create Virtual fields, go back to the Layer menu, export it and open the exported file).&lt;br /&gt;
* Select the following options:&lt;br /&gt;
** Update Existing Field&lt;br /&gt;
** Select the Landclass field you just created.&lt;br /&gt;
** Copy the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/corine_vector.txt into the Expression box (without the comment lines starting with &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt;).  This is just some simple code to set the attribute correctly.  The code should be correct for CORINE vector data.  If your data is from other sources you will need to work out how you want to map your source data landclasses to the CORINE ones.  [https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Materials/base/landclass-mapping.xml Materials/base/landclass-mapping.xml] can be used as a guide.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.  You should see that your landclass column is now populated with the landclass data.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
=== Creating a Raster from a Vector Layer ===&lt;br /&gt;
To create a Raster from a Vector Layer select &amp;lt;code&amp;gt;Raster-&amp;gt;Conversion-&amp;gt;Rasterize (Vector to Raster)&amp;lt;/code&amp;gt;.  &lt;br /&gt;
[[File:QGIS Rasterize (Vector to Raster).png|thumb|Creating a Raster from a Vector Layer - QGIS Rasterize]]&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Field to use for burn-in value - select the Landclass column you created above.&lt;br /&gt;
* Output raster size units.  This is going to set the resolution of your raster.  You can work out the resolution in two different ways:&lt;br /&gt;
** Select &amp;quot;Georeferenced units&amp;quot; and determine how many degrees each pixel is in latitude and longitude.&lt;br /&gt;
** Select &amp;quot;Pixels&amp;quot; and determine the size of raster you want in pixels.  [https://www.nhc.noaa.gov/gccalc.shtml This] is a good calculator to help.&lt;br /&gt;
* Width/Horizontal Resolution.  Enter the values you've calculated for the horizontal resolution (longitudinal), or the width of the raster&lt;br /&gt;
* Height/Vertical Resolution.    Enter the values you've calculated for the vertical resolution (latitude or the height of the raster&lt;br /&gt;
* Output extent - Select an option from the box on the right.    You can edit the text afterwards.  Best practise is to create long thin strips of 1 degree latitude in height, as this makes subsequent processing much easier.&lt;br /&gt;
* Assign a specific nodata value to output bands - Select 0.0 for Ocean.  CORINE vector data in particular has a lot of nodata for Oceans&lt;br /&gt;
* Advanced Parameters - No Compression&lt;br /&gt;
* Output data type - Byte&lt;br /&gt;
* Rasterized - Select a new filename&lt;br /&gt;
&lt;br /&gt;
=== Simplifying a Raster Layer ===&lt;br /&gt;
Some Raster Landclass data (NLCD included) has too much noise - in particular large US highway systems are identified as Urban areas.&lt;br /&gt;
&lt;br /&gt;
To smooth it out we can use the GRASS &amp;lt;code&amp;gt;n.neighbors&amp;lt;/code&amp;gt; function from the Processing Toolbox in QGIS.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Layer - correct layer, check CRS&lt;br /&gt;
* Neighborhood operation - median.  (This is not a normal image, so using an average will result in weird values)&lt;br /&gt;
* Neighborhood size - 5.&lt;br /&gt;
* Neighbors - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
=== Clipping a Raster Layer with OSM Data for Land (Corine) ===&lt;br /&gt;
The Corine dataset does not match OSM coastlines exactly. The following multi-stage process makes sure, that no Corine land-use is in the water as defined by OSM. &lt;br /&gt;
&lt;br /&gt;
==== Download OSM Land Data ====&lt;br /&gt;
&lt;br /&gt;
Download land polygons based on OSM data as a Shapefile from [https://osmdata.openstreetmap.de/data/land-polygons.html Land Polygons] and make sure to pick the WGS84 projected download with split polygons (&amp;quot;Large polygons are split, use for larger scales&amp;quot;). Once downloaded unzip the content into a directory.&lt;br /&gt;
&lt;br /&gt;
==== Reclassifying the OSM Land Data Vector Layer ====&lt;br /&gt;
To do this: &lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Open Attribute Table&amp;lt;/code&amp;gt;. You should see a table with multiple columns. Each row represents a feature in the data.&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;Toggle Editing Mode&amp;lt;/code&amp;gt; (Ctrl + E), on the top left of the dialogue&lt;br /&gt;
* Click on &amp;lt;code&amp;gt;New Field&amp;lt;/code&amp;gt; (Ctrl+W) and create a new field called Landclass of type &amp;lt;code&amp;gt;Integer (32 bit)&amp;lt;/code&amp;gt;. This will create a new column which we will populate with the correct land class data&lt;br /&gt;
* On top of the table on the left side choose &amp;quot;Landclass&amp;quot; in the drop-down menu, then input &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; into the field to the right and then press button &amp;quot;Update&amp;quot; all to the left of this field.&lt;br /&gt;
* Wait a bit and the close the dialogue.&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Layer-&amp;gt;Save Layer Edits&amp;lt;/code&amp;gt; to save you changes&lt;br /&gt;
&lt;br /&gt;
==== Convert the Land Data from Vector to Raster ====&lt;br /&gt;
Do the same as in chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; above. The only difference is that the Input layer will be the land data polygons and you need to choose a different file name for the &amp;quot;Rasterized&amp;quot; (e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
&lt;br /&gt;
==== Remove Novalue Entries in the Land Data Raster ====&lt;br /&gt;
To do this:&lt;br /&gt;
&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Processing-&amp;gt;Toolbox&amp;lt;/code&amp;gt;. You should see a new box on the right side.&lt;br /&gt;
* Write &amp;quot;gdal_calc&amp;quot; in the search box and you should see an entry &amp;quot;Raster calculator&amp;quot;. Double click on it and you will get a new dialogue window.&lt;br /&gt;
* In this dialogue:&lt;br /&gt;
** For &amp;quot;Input layer A&amp;quot; choose the raster from the previous chapter ((e.g. osm_land_scotland_5m.tif)&lt;br /&gt;
** In field &amp;quot;Calculation in gdalnumeric ...&amp;quot; write: &amp;lt;code&amp;gt;greater(A,0) * A&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Output raster type&amp;quot; choose &amp;lt;code&amp;gt;Byte&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Advanced Parameters&amp;quot; choose Profile &amp;lt;code&amp;gt;No compression&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Additional command-line parameters&amp;quot; write: &amp;lt;code&amp;gt;--hideNoData&amp;lt;/code&amp;gt;&lt;br /&gt;
** In field &amp;quot;Calculated&amp;quot; choose a file (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
** (In the &amp;quot;GDAL/OGR console call&amp;quot; it will have something similar to the follwing - just with different paths: &amp;lt;code&amp;gt;gdal_calc.py --overwrite --calc &amp;quot;greater(A ,0) * A&amp;quot; --format GTiff --type Byte -A /home/vanosten/custom-fg-scenery/data/osm_land_scotland_5m.tif --A_band 1 --co COMPRESS=NONE --co BIGTIFF=IF_NEEDED --hideNoData --outfile /home/vanosten/custom-fg-scenery/data/osm_land_scotland_allvalues_5m.tif&amp;lt;/code&amp;gt;&lt;br /&gt;
** Press the &amp;quot;Run&amp;quot; button - and when complete close the dialogue.&lt;br /&gt;
&lt;br /&gt;
You should now see a map only black and white. You can check for correctness by pressing &amp;lt;code&amp;gt;CTRL+SHIFT+I&amp;lt;/code&amp;gt; to get a cursor with an arrow and an &amp;quot;i&amp;quot;. First make sure the new raster is selected on the left side. Next click on the sea/ocean and then check in the &amp;quot;Identify Results&amp;quot; window on the right that the value is &amp;lt; 2. The click on the land and check that the value is 2.&lt;br /&gt;
&lt;br /&gt;
==== Create the Final Clipped Corine Raster Against OSM Land Data =====&lt;br /&gt;
Do the following:&lt;br /&gt;
&lt;br /&gt;
* In QGIS make sure that you have only the following two layers: the basis Corine raster (see chapter &amp;quot;Creating a Raster from a Vector Layer&amp;quot; - here e.g. corine_raster_scotland_5m.tif) and plus the raster from the previous step (e.g. osm_land_scotland_allvalues_5m.tif)&lt;br /&gt;
* Select &amp;lt;code&amp;gt;Raster-&amp;gt;Raster Calculator ...&amp;lt;/code&amp;gt; and a corresponding dialogue will open showing on the left hand side the two rasters.&lt;br /&gt;
* Choose a new &amp;quot;Output layer&amp;quot; (e.g. corine_raster_scotland_clipped_5m.tif).&lt;br /&gt;
* In the &amp;quot;Raster Calculator Expression&amp;quot; field input: &amp;lt;code&amp;gt;if (&amp;quot;osm_land_scotland_all_data_5m@1&amp;quot; &amp;lt; 2, 44, &amp;quot;corine_raster_scotland_5m@1&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Press button &amp;quot;OK&amp;quot; and wait a while (you will see a new dialogue with showing the progress.&lt;br /&gt;
&lt;br /&gt;
Done. You have now a raster (e.g. corine_raster_scotland_clipped_5m.tif) which does not have land in areas, where OSM data has sea/ocean.&lt;br /&gt;
&lt;br /&gt;
=== Reclassifying a Raster Layer ===&lt;br /&gt;
WS3.0 uses CORINE landclass values.  If using data from other sources it needs to be reclassified to the correct values.  genVPB.py has an option to do this, but you may wish to do so manually.  &lt;br /&gt;
&lt;br /&gt;
To do this select &amp;lt;code&amp;gt;GRASS-&amp;gt;Raster-&amp;gt;r.reclass&amp;lt;/code&amp;gt; from the Processing Toolbox.&lt;br /&gt;
&lt;br /&gt;
Select the following options:&lt;br /&gt;
&lt;br /&gt;
* Input Raster Layer - correct layer, check CRS&lt;br /&gt;
* Reclass rules text - copy in the contents of https://sourceforge.net/p/flightgear/fgmeta/ci/next/tree/ws30/mappings/nlcd2019.txt.  Or an appropriate mapping from your landclass data to CORINE.  Note that you can also reference a file using the &amp;quot;File containing reclass rules&amp;quot; option. Note a mapping of 22 24 = 1 is the same as 22 and 24 = 1. For a range of 22 to 24 use 22 23 24 = 1.&lt;br /&gt;
* Reclassified - Select a new filename.&lt;br /&gt;
&lt;br /&gt;
(If this doesn't work a similar function is available in the Processing Toolbox under &amp;lt;code&amp;gt;Raster analysis-&amp;gt;Reclassify by table&amp;lt;/code&amp;gt;.  However this doesn't save your table once you close the dialog, and entries have to be manually entered individually which takes a lot of effort)&lt;br /&gt;
&lt;br /&gt;
=== Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator, Up sampling and GRASS r.neighbors===&lt;br /&gt;
&lt;br /&gt;
We will use a predetermined file naming convention throughout this procedure for simplicity. You can use a naming convention of your choice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2019_Land_Cover from source of choice, https://www.mrlc.gov/viewer/&lt;br /&gt;
&lt;br /&gt;
NLCD_2019_Land_Cover data = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Obtain your NLCD_2016_Tree_Canopy from source of choice, https://www.mrlc.gov/viewer/ &lt;br /&gt;
&lt;br /&gt;
NLCD_2016_Tree_Canopy data = NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Deciduous Layer 255 to 0 EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; = 255) * 0 + (&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 255) * &amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous-coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Tree Canopy Layer EPSG:5070 - NAD83 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
Use the prevailing tree type, for Deciduous use * 41, for Evergreen use  * 42, for MixedForest use 43.&lt;br /&gt;
&lt;br /&gt;
Later we add this Tree Canopy layer to the raster where there is currently no trees in the NLCD&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255 - &lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_deciduous-coast-clipped@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;NLCD_2016_Tree_Canopy_California-Southern@1&amp;quot; != 0) * 42&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:5070 - NAD83&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Clipped Ocean Frontage EPSG:5070 - NAD83 - GIMP&lt;br /&gt;
&lt;br /&gt;
Source = NLCD_2019_Land_Cover_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
Flood fill index 11 (blue) with 0 (black)&lt;br /&gt;
&lt;br /&gt;
Export tiff =  California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Warp NLCD Deciduous and Base NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84 - WARP&lt;br /&gt;
&lt;br /&gt;
If ocean bordering with index 255&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_coast-clipped.tiff&lt;br /&gt;
&lt;br /&gt;
Else&lt;br /&gt;
&lt;br /&gt;
Warp NLCD_2016_Tree_Canopy_California-Southern.tiff&lt;br /&gt;
&lt;br /&gt;
and&lt;br /&gt;
&lt;br /&gt;
Warp Deciduous NLCD from EPSG:5070 - NAD83 / Conus Albers NLCD to Project CRS: EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Warp California-Southern_deciduous.tiff&lt;br /&gt;
&lt;br /&gt;
Raster -&amp;gt; Projection - &amp;gt; Warp&lt;br /&gt;
&lt;br /&gt;
Target CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Resampling method to use = Nearest Neighbour&lt;br /&gt;
&lt;br /&gt;
Output data type = byte&lt;br /&gt;
&lt;br /&gt;
Reprojected = California-Southern_4326-84.tiff and California-Southern_deciduous_4326-84.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_deciduous_4326-84.@1&amp;quot; and &amp;quot;California-Southern_4326-84@1&amp;quot; - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step adds the Tree Canopy layer to the existing NLCD raster only where there is currently no existing tree land cover.&lt;br /&gt;
&lt;br /&gt;
((&amp;quot;California-Southern_4326-84@1&amp;quot; &amp;gt; 0 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 41 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 42 AND &amp;quot;California-Southern_4326-84@1&amp;quot; != 43) AND &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;gt; 0) * &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; + (&amp;quot;California-Southern_4326-84@1&amp;quot; = 41 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 42 OR &amp;quot;California-Southern_4326-84@1&amp;quot; = 43 OR &amp;quot;California-Southern_deciduous_4326-84@1&amp;quot; &amp;lt;= 0) * &amp;quot;California-Southern_4326-84@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Re-class urban 21, 22, 23, 24 = grass 26 - Raster Calculator&lt;br /&gt;
&lt;br /&gt;
This step is being used to erase all the man made clutter on the raster with grassland. Instead of grassland you can replace it with any type of prevailing groundcover in the area your processing.&lt;br /&gt;
&lt;br /&gt;
For example this places a road easement of grass on all the road clutter that was originally in the raster. If your processing a desert area you might want to use dirt or sand instead of grassland. For Arctic areas maybe use tundra.&lt;br /&gt;
&lt;br /&gt;
Part of the clutter gets replaced later in the process.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 11) * 41 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 12) * 34 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 31) * 27 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 41) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 42) * 24 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 43) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 51) * 30 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 52) * 29 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 71) * 26 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 72) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 73) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 74) * 31 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 75) * 32 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 81) * 18 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 82) * 19 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 90) * 25 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 95) * 35&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_reclass-grass.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Make Urban layer&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_adjusted@1&amp;quot; = 21) * 10 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 22) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 23) * 1 + (&amp;quot;California-Southern_adjusted@1&amp;quot; = 24) * 2&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_urban.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Remove the road clutter with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Combine &amp;quot;California-Southern_reclass-urban@1&amp;quot; and &amp;quot;California-Southern_reclass-grass@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This step replaces the &amp;quot;cleaned up&amp;quot; urban areas back into the raster.&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_urban-only@1&amp;quot; &amp;lt; 1) * &amp;quot;California-Southern_reclass-grass@1&amp;quot; + (&amp;quot;California-Southern_urban-only@1&amp;quot; != 0) * &amp;quot;California-Southern_urban-only@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Up sample California-Southern_adjusted-combined.tiff 2x, 4x or 8x resolution 0.00008309125&lt;br /&gt;
&lt;br /&gt;
Active Layer = California-Southern_adjusted-combined.tiff&lt;br /&gt;
&lt;br /&gt;
Layer/Save As&lt;br /&gt;
&lt;br /&gt;
2x Horizontal = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
2x Vertical = 0.00008309125&lt;br /&gt;
&lt;br /&gt;
4x Horizontal = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
4x Vertical = 0.00010637625&lt;br /&gt;
&lt;br /&gt;
8x Horizontal = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
8x Vertical = 0.00005318812&lt;br /&gt;
&lt;br /&gt;
File Name = California-Southern_final-prep-4x.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Simplify California-Southern_4326-84-hd.tiff with r.neighbor&lt;br /&gt;
&lt;br /&gt;
GRASS/Raster/r.neighbor&lt;br /&gt;
&lt;br /&gt;
Neighborhood operation = median&lt;br /&gt;
&lt;br /&gt;
Neighborhood size = 7&lt;br /&gt;
&lt;br /&gt;
Neighbors = California-Southern_urban-only.tiff&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reclass index 0 to 44, leave the rest the same&lt;br /&gt;
&lt;br /&gt;
(&amp;quot;California-Southern_4326-84-hd@1&amp;quot; = 0) * 44 + (&amp;quot;California-Southern_4326-84-hd@1&amp;quot; != 0) * &amp;quot;California-Southern_4326-84-hd@1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Output CRS = EPSG:4326 - WGS 84&lt;br /&gt;
&lt;br /&gt;
Output Layer = California-Southern_4326-84-hd-corrected.tiff&lt;br /&gt;
&lt;br /&gt;
OK&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;Optional HD Fresh Water Option&amp;lt;/big&amp;gt; &lt;br /&gt;
*Step 1 - Obtain and load hi resolution vector layer&lt;br /&gt;
Make sure that the vector layer and the raster layer you will eventually merge to have the same projection. ** Extent can be different if you use the option below.&lt;br /&gt;
#Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot; or Processing Toolbox: GDAL -&amp;gt; &amp;quot;Vector conversion&amp;quot; -&amp;gt; &amp;quot;Rasterize (vector to raster)&amp;quot;&lt;br /&gt;
# Input layer = Southern-California_water_4326-84&lt;br /&gt;
#Fixed value to burn = 41 (water)&lt;br /&gt;
# Output raster size units = &amp;quot;Georeferenced units&amp;quot;&lt;br /&gt;
#Width/Horizontal resolution = 0.00008309125 (4x the base NLCD resolution )&lt;br /&gt;
#Height/Vertical resolution = 0.00008309125 (4x the base NLCD resolution)&lt;br /&gt;
#&amp;lt;nowiki&amp;gt;** Output extent = Select the raster layer you will eventually merge with as the &amp;quot;Output extent&amp;quot;.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
#Under &amp;quot;Rasterized&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-water.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
*Step 2 - Reclass hi res, smoothed water to grass&lt;br /&gt;
# Open Raster Calculator&lt;br /&gt;
#Under &amp;quot;Output layer&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hd-nowater.tiff.&lt;br /&gt;
#Copy the following into the &amp;quot;Raster Calculator Expression&amp;quot; box. Where Southern-California_4326-84-hd is the name of your hi resolution, smoothed NLCD that includes the water data.&lt;br /&gt;
&amp;quot;Southern-California_4326-84-hd@1&amp;quot; * (&amp;quot;Southern-California_4326-84-hd@1&amp;quot; != 41) + 26 * (&amp;quot;Southern-California_4326-84-hd@1 = 41&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    Click on &amp;quot;OK&amp;quot;. When finished you will have a new raster with the water layer changed to grassland. Another choice would be to change it to sand. &lt;br /&gt;
&lt;br /&gt;
*Step 3 - Combine the hi resolution no water raster and the hi resolution water raster.&lt;br /&gt;
# Use Top Menu: &amp;quot;Raster&amp;quot; -&amp;gt; &amp;quot;Miscellaneous&amp;quot; -&amp;gt; &amp;quot;Merge&amp;quot; or Processing Toolbax: GDAL -&amp;gt; Raster Miscellaneous -&amp;gt; Merge&lt;br /&gt;
#Input layers = Select &amp;quot;Southern-California_4326-84-hd-nowater@1&amp;quot; and Southern-California_4326-84-hd-water.tiff&lt;br /&gt;
#Output data type = &amp;quot;byte&amp;quot;.&lt;br /&gt;
# Under &amp;quot;Merged&amp;quot; choose a new filename to save the new raster to. We'll use Southern-California_4326-84-hdwater.tiff.&lt;br /&gt;
&lt;br /&gt;
    Click &amp;quot;Run&amp;quot; to generate the new merged hi resolution raster layer.&lt;br /&gt;
&lt;br /&gt;
==='''Optional Python script''' to process NLCD for the USA===&lt;br /&gt;
You can optionally process the NLCD by loading this script in the python console of the QGIS Desktop program.&lt;br /&gt;
&lt;br /&gt;
If you save the NLCD tiff's using the same consistent naming convention used in this example it is fairly simple to generate final WS3.0 tiff's to use to generate the scenery.&lt;br /&gt;
&lt;br /&gt;
You will start with an original tree layer and a land cover layer from the MRLC.gov site. See the section &amp;quot;Obtaining NLCD&amp;quot; in the above &amp;quot;Step By Step Procedure for Processing NLCD for the USA using the Raster Calculator&amp;quot; topic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For example, to start with you will have a couple files named...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork.tiff&lt;br /&gt;
&lt;br /&gt;
In the QGIS program the layer names you start with will be...&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Tree_Canopy_NewYork&lt;br /&gt;
&lt;br /&gt;
NLCD_2021_Land_Cover_NewYork&lt;br /&gt;
&lt;br /&gt;
which will refer to the tiff files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Running this script will generate a bunch of intermediate files including the last file, that will be the final finished file for running in the VPB script or VPB build environment Docker image.&lt;br /&gt;
&lt;br /&gt;
The last file for this example for New York would be, NLCD_2021_Land_Cover_NewYork_Final-HD_4326.tiff&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
from qgis.core import QgsProject, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProcessingException&lt;br /&gt;
from qgis.analysis import QgsRasterCalculator, QgsRasterCalculatorEntry&lt;br /&gt;
from osgeo import gdal, osr&lt;br /&gt;
import os&lt;br /&gt;
import numpy&lt;br /&gt;
import numpy as np&lt;br /&gt;
import processing&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
# Define input layer names, change according to your file names&lt;br /&gt;
year = '2021';&lt;br /&gt;
state = 'Kentucky';&lt;br /&gt;
part = '';&lt;br /&gt;
&lt;br /&gt;
tree_canopy_layer_name = 'NLCD_' + year +  '_Tree_Canopy_' + state + part&lt;br /&gt;
land_cover_layer_name = 'NLCD_' + year +  '_Land_Cover_' + state + part&lt;br /&gt;
&lt;br /&gt;
extent = None&lt;br /&gt;
&lt;br /&gt;
################################################## Step 1: Reclass tree canopy to one landcover type 41, 42, or 43 #########################################&lt;br /&gt;
&lt;br /&gt;
# Define input and output paths&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Trees-Combined.tiff'&lt;br /&gt;
    &lt;br /&gt;
# Retrieve input layers from the project using iface for land cover layer&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(tree_canopy_layer_name)[0]&lt;br /&gt;
# Set the layers as the active layer&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
# Retrieve input layers from the project using iface&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()  &lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A &amp;gt; 0) * 41 + '&lt;br /&gt;
    '(A &amp;lt;= 0) * A'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Trees-Combined')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 1: Calculate result_tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Trees-Combined)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################# Step 2: Warp tree canopy to 4326 #############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Trees-Combined')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_tree_canopy.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 2: Warp tree_canopy completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Tree_Canopy_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################### Step 3: Warp land cover 4326 ############################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Land_Cover_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName(land_cover_layer_name)[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;gdal:warpreproject&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'INPUT':input_layer_land_cover.source(),&lt;br /&gt;
        'SOURCE_CRS':None,&lt;br /&gt;
        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),&lt;br /&gt;
        'RESAMPLING':0,&lt;br /&gt;
        'NODATA':None,&lt;br /&gt;
        'TARGET_RESOLUTION':None,&lt;br /&gt;
        'OPTIONS':'',&lt;br /&gt;
        'DATA_TYPE':1,&lt;br /&gt;
        'TARGET_EXTENT':extent,&lt;br /&gt;
        'TARGET_EXTENT_CRS':None,&lt;br /&gt;
        'MULTITHREADING':False,&lt;br /&gt;
        'EXTRA':'',&lt;br /&gt;
        'OUTPUT':output_path&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 3: Warp land_cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Land_Cover_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################ Step 4: Combine land cover 4326 and tree canopy 4326######################################&lt;br /&gt;
  &lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
warped_tree_canopy_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Tree_Canopy_4326')[0]&lt;br /&gt;
warped_land_cover_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Land_Cover_4326')[0]&lt;br /&gt;
iface.setActiveLayer(warped_tree_canopy_layer)&lt;br /&gt;
input_layer_tree_canopy = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(warped_land_cover_layer)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((B &amp;gt; 0) &amp;amp; (B != 41) &amp;amp; (B != 42) &amp;amp; (B != 43) &amp;amp; (A &amp;gt; 0)) * A + '&lt;br /&gt;
    '((B == 41) | (B == 42) | (B == 43) | (A &amp;lt;= 0)) * B'&lt;br /&gt;
   )&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_layer_tree_canopy.source(),&lt;br /&gt;
    '-B', input_layer_land_cover.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_tree_canopy_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Combined_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_tree_canopy_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 4: Combine tree canopy and land cover completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 5: Replace urban and clutter with grass ###################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Grass-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 11) * 41 + '&lt;br /&gt;
    '(A == 12) * 34 + '&lt;br /&gt;
    '(A == 21) * 26 + '&lt;br /&gt;
    '(A == 22) * 26 + '&lt;br /&gt;
    '(A == 23) * 26 + '&lt;br /&gt;
    '(A == 24) * 26 + '&lt;br /&gt;
    '(A == 31) * 27 + '&lt;br /&gt;
    '(A == 41) * 23 + '&lt;br /&gt;
    '(A == 42) * 24 + '&lt;br /&gt;
    '(A == 43) * 25 + '&lt;br /&gt;
    '(A == 51) * 30 + '&lt;br /&gt;
    '(A == 52) * 29 + '&lt;br /&gt;
    '(A == 71) * 26 + '&lt;br /&gt;
    '(A == 72) * 32 + '&lt;br /&gt;
    '(A == 73) * 31 + '&lt;br /&gt;
    '(A == 74) * 31 + '&lt;br /&gt;
    '(A == 75) * 32 + '&lt;br /&gt;
    '(A == 81) * 18 + '&lt;br /&gt;
    '(A == 82) * 19 + '&lt;br /&gt;
    '(A == 90) * 25 + '&lt;br /&gt;
    '(A == 95) * 35'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_grass_only_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_grass_only_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 5: Calculate result_grass_only completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Grass-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
################################################################## Step 6: Reclass urban 21, 22, 23 or 24 ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
combined_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined_4326')[0]&lt;br /&gt;
iface.setActiveLayer(combined_layer)&lt;br /&gt;
input_combined_layer = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '(A == 21)*10 + '&lt;br /&gt;
    '(A == 22)*1 + '&lt;br /&gt;
    '(A == 23)*1 + '&lt;br /&gt;
    '(A == 24)*2'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_combined_layer.source(),&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_urban_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_urban_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 6: Calculate result_urban completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################ Step 7: Remove clutter and roads from urban ###########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Urban-Only_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(output_path, 'NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 7: Remove clutter and roads from urban. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Urban-Only_4326)&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
############################################################## Step 8: Combine grass only and clean urban #########################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_Combined-Clean_4326.tiff'&lt;br /&gt;
&lt;br /&gt;
grass_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Grass-Only_4326')[0]&lt;br /&gt;
urban_only_layer = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Urban-Only_4326')[0]&lt;br /&gt;
iface.setActiveLayer(urban_only_layer)&lt;br /&gt;
input_urban_only = iface.activeLayer()&lt;br /&gt;
iface.setActiveLayer(grass_only_layer)&lt;br /&gt;
input_grass_only = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
expression = (&lt;br /&gt;
    '((A &amp;gt; 0) &amp;amp; (B &amp;gt; 0)) * A + '&lt;br /&gt;
    '((A &amp;lt;= 0) | (B &amp;lt;= 0)) * B + (B == 0) * 44'&lt;br /&gt;
)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_calc.py',&lt;br /&gt;
    '-A', input_urban_only.source(),&lt;br /&gt;
    '-B', input_grass_only.source(),&lt;br /&gt;
    '--type=Byte',&lt;br /&gt;
    '--outfile', output_path,&lt;br /&gt;
    '--calc', expression&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
# Resample the combined raster to a different resolution&lt;br /&gt;
output_path_resampled = output_path.replace(&amp;quot;_4326.tiff&amp;quot;, &amp;quot;_HD_4326.tiff&amp;quot;)&lt;br /&gt;
gdal.Warp(&lt;br /&gt;
    output_path_resampled,&lt;br /&gt;
    output_path,&lt;br /&gt;
    xRes=0.00005539416,&lt;br /&gt;
    yRes=0.00005539416&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
urban_grass_combined = QgsRasterLayer(output_path_resampled, 'NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(urban_grass_combined)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 8: Combined and clean completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Combined-Clean_HD_4326)&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
################################################################## Step 9: Smooth all hi res features #################################################################&lt;br /&gt;
&lt;br /&gt;
output_path = 'G:/Scenery/ws3.0/' + state + '/data/NLCD_' + year +  '_' + state + part + '_HD_4326.tiff'&lt;br /&gt;
    &lt;br /&gt;
layer1 = QgsProject.instance().mapLayersByName('NLCD_' + year +  '_' + state + part + '_Combined-Clean_HD_4326')[0]&lt;br /&gt;
iface.setActiveLayer(layer1)&lt;br /&gt;
input_layer_land_cover = iface.activeLayer()&lt;br /&gt;
&lt;br /&gt;
processing.run(&lt;br /&gt;
    &amp;quot;grass7:r.neighbors&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        'input':input_layer_land_cover.source(),&lt;br /&gt;
        'selection':None,&lt;br /&gt;
        'method':1,&lt;br /&gt;
        'size':7,&lt;br /&gt;
        'gauss':None,&lt;br /&gt;
        'quantile':'',&lt;br /&gt;
        '-c':False,&lt;br /&gt;
        '-a':False,&lt;br /&gt;
        'weight':'',&lt;br /&gt;
        'output':output_path,&lt;br /&gt;
        'GRASS_REGION_PARAMETER':extent,&lt;br /&gt;
        'GRASS_REGION_CELLSIZE_PARAMETER':0,&lt;br /&gt;
        'GRASS_RASTER_FORMAT_OPT':'',&lt;br /&gt;
        'GRASS_RASTER_FORMAT_META':''&lt;br /&gt;
    }&lt;br /&gt;
)  &lt;br /&gt;
&lt;br /&gt;
# Convert output to Byte using gdal_translate&lt;br /&gt;
converted_output_path = output_path.replace(&amp;quot;_HD_4326.tiff&amp;quot;, &amp;quot;_Final-HD_4326.tiff&amp;quot;)&lt;br /&gt;
command = [&lt;br /&gt;
    'gdal_translate',&lt;br /&gt;
    '-ot', 'Byte',  # Set output data type to Byte&lt;br /&gt;
    output_path,&lt;br /&gt;
    converted_output_path&lt;br /&gt;
]&lt;br /&gt;
subprocess.run(command, shell=True)&lt;br /&gt;
&lt;br /&gt;
result_land_cover_warped_layer = QgsRasterLayer(converted_output_path, 'NLCD_' + year +  '_' + state + part + '_Final-HD_4326')&lt;br /&gt;
QgsProject.instance().addMapLayer(result_land_cover_warped_layer)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;Step 9: Smooth all hi res features completed. (NLCD_&amp;quot; + year +  &amp;quot;_&amp;quot; + state + part + &amp;quot;_Final-HD_4326)&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Generating the Terrain using osgdem===&lt;br /&gt;
Instead of using genVPB.py, you may wish to run osgdem directly.&lt;br /&gt;
&lt;br /&gt;
In the Windows/Docker platform you can send the generate tile command directly to osgdem.exe, one tile at a time.&lt;br /&gt;
&lt;br /&gt;
Using the NLCD raster processing convention from above, following is the the final step after creating the raster and entering bash shell with the windows version of &amp;quot;docker run...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 osgdem --TERRAIN --image-ext png --RGBA --no-interpolate-imagery --disable-error-diffusion --geocentric --no-mip-mapping -t ./data/California-Southern_4326-84-hd-corrected.tiff -d ./SRTM-3/N32W115.hgt -b -115 32 -114 33 --PagedLOD -l 7 --radius-to-max-visible-distance-ratio 3 -o ./output/vpb/w120n30/w115n32/ws_w115n32.osgb&lt;br /&gt;
&lt;br /&gt;
Note: the --image-ext png --RGBA flags are critical to successfully building correctly placed landclasses in the final VPB generated scenery.&lt;br /&gt;
&lt;br /&gt;
If you prefer to run the scenery generation manually, running the VPB osgdem process is described in more detail here: [[Virtual Planet Builder#Running VPB]].&lt;br /&gt;
&lt;br /&gt;
After doing this you should have an output directory containing files of the form &amp;lt;code&amp;gt;output/vpb/w010n50/w004n50/ws_w004n50.osgb&amp;lt;/code&amp;gt;, plus a host of sub-directories. Each one of these is a 1x1 tile of terrain.  &lt;br /&gt;
&lt;br /&gt;
to leave the container simply type &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Packaging the Scenery===&lt;br /&gt;
Once you have the terrain and line features they should be packaged in a scenery directory in vpb and Terrain sub-directories respectively.  E.g.&lt;br /&gt;
 MyCoolScenery/Terrain&lt;br /&gt;
 MyCoolScenery/vpb&lt;br /&gt;
It is good practise to document the data sources used in scenery generation.  Some source licenses require attribution of the original data source for anything derived, published or distributed.   &lt;br /&gt;
&lt;br /&gt;
To assist in fulfilling these license obligations, you can create a source.xml file in the scenery directory which includes attribution information.  This will then be available from within the simulator under Help-&amp;gt;Scenery Sources, and &amp;lt;u&amp;gt;may&amp;lt;/u&amp;gt; fulfil the attribution requirements of your license.  '''Note that you are responsible for fulfilling any license requirements from the data, not FlightGear'''.  &lt;br /&gt;
&lt;br /&gt;
The format of the file is straightforward:&lt;br /&gt;
 &amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
 &amp;lt;PropertyList&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;Corine Land Cover (CLC) 2018, Version 2020_20u1&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;http://web.archive.org/web/20221112175615/https://land.copernicus.eu/pan-european/corine-land-cover/clc2018?tab=metadata%2A&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;GMES Open License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;NASADEM Merged DEM Global 1 arc second V001&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.earthdata.nasa.gov/&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Public Domain&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;name&amp;gt;OpenStreetMap&amp;lt;/name&amp;gt;&lt;br /&gt;
         &amp;lt;nowiki&amp;gt;&amp;lt;link&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;https://www.openstreetmap.org/copyright&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/link&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         &amp;lt;license&amp;gt;Open Data Commons Open Database License&amp;lt;/license&amp;gt;&lt;br /&gt;
     &amp;lt;nowiki&amp;gt;&amp;lt;/source&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 &amp;lt;/PropertyList&amp;gt;&lt;/div&gt;</summary>
		<author><name>Wlbragg</name></author>
	</entry>
</feed>