# OpenStreetMap data

OpenStreetMap or OSM data is available worldwide. OSM elements define and tag the world's features.

OSM has three primary types of elements available:

Element Remarks
node A node represents a single point.
Sometimes featured as tagged as a node if they are small, but often larger features will have their perimeter defined by a way instead. A node may be a point on the road or rail network, or a tree or a hospital building.
You'll need to be careful as it is becoming increasingly common to tag features with a way instead of the center point. By adding out center you an ask OSM to give you the center point for a way, which can sometimes be helpful also.
way A way is a container for a list of nodes.
Roads and Railways are created from ways which are created from nodes. You'll be able to retrieve the way and its metadata, as well as the complete list of nodes contained by that way. An area is a closed way.
relation A relation is a way to include nodes an ways into a type of container which may have tags. A relation may be used for an administrative boundary like the city or state.

# OSM Developer Workflow

You'll need to:

  1. Discover features that you wish to work with. Visit OpenStreetMap (opens new window) and use the Query Features tool (right edge) to examine OSM data at a given location.
  2. Create the query that will retrieve those features. Visit Overpass Turbo (opens new window) to start working with queries and testing them in different locations on the map.
  3. Choose the way you want to use the data within the mission. You might use create_location and then query_random_result (get all results from the query, pick one randomly) or query_closest_result (start at 100 meters and expand until you find the first result).

Once you found the data you like and created a working query, you'll integrate that into the mission and test it.

# Example OSM queries

All queries below will use the bbox bounding box. Don't forget that you must escape quotes " like this: \" in JSON.

Get nearby hospitals (amenity=hospital tag). We are getting node and way results, because hospitals are tagged often each way. out center is adding a centroid point to each way which enables us to treat them more similarly to nodes.

[out:json];
(
  node({{bbox}})[amenity=hospital];
  way({{bbox}})[amenity=hospital];
);
out center;

Breakdown of the query:

  1. [out:json];: Configure for JSON output. Always the same.
  2. ( and );: Union that will combine the two groups of results into one list.
  3. Get nodes within the view which match the tag amenity=hospital.
node({{bbox}})[amenity=hospital];
  1. : Get ways within the view which match the tag amenity=hospital.
way({{bbox}})[amenity=hospital];
  1. out center;: For ways, add a centroid lat/lon to each result.

# OpenStreetMap APIs

There are powerful APIs for working with data available from OpenStreetMap. You can discover nearby POIs as well as examine polygons and other relationships.

NOTE You'll want to always use bbox which will be replaced for you by the location/radius provided. Convert any code using around to bbox.

API V1 or V2 Remarks
create_location V1 Create a location from a list of zones. Still useful.
query_data V1 Still useful for database query.
query_country V1 Get the country for a given locatioin. Still useful.
osm_query_data V2 Query and then process any OSM OverpassAPI data

When using the V2 API, you'll use these accessors:

API Remarks
osm_get_parent_ways Get the ways that a given node exists in
osm_get_connected_nodes Get the nodes before and after this node in the ways it exists in
osm_get_nodes Get the nodes within this way
osm_get_all_ways Get all the ways within the data set
osm_get_all_nodes Get all the nodes within the data set
osm_get_closest_nodes Get an ordered list of nodes, order is by distance
osm_is_point_within_way Get a boolean indicating whether the point is within a given closed way
osm_get_area_of_area Get the area in meters^2 of a given closed way

# Show nearby hospital helipads sample

This sample uses query_data to find nearby items in the DB:H_HOSPITAL (hospital with helipad) database.

{
  "title": "Show nearby hospital helipads",
  "author":"davux3",
  "api_version": 0.1,
  "aircraft": ["H145"],

  "objectives": [
    {
      "title": "Done",
      "commands": [
        
        {"query_data": {
          "query": "DB:H_HOSPITAL",
          "location": "$USER",
          "radius": 5000,
          "minRadius": 0,
          "bypass_commands": [
            {"#comment":"$ITEMS contains an array of results: type=way, lat, lon, tags. by convention center is copied down into lat/lon"},
            {"for_each":{"param":"$ITEMS"},"do":[
              {"set":{"param":"loc"},"value":{"create_array":[
                {"struct":{"param":"$item"},"path":"lat"},
                {"struct":{"param":"$item"},"path":"lon"}
              ]}},
              {"set_map":{"add":{"point":{"location":{"param":"loc"},"icon":"ki_helipad"}}}}
            ]}
          ]
        }},

        {"sleep": "forever"}
      ]
    }
  ]
}

# Show nearby power substations sample

This sample uses query_data to find nearby OSM items matching power=substation.

{
  "title": "Show nearby power substations",
  "author":"davux3",
  "api_version": 0.1,
  "aircraft": ["H145"],
  "objectives": [
    {
      "title": "Done",
      "commands": [
        
        {"query_data": {
          "query": "[out:json]; (node({{bbox}})[power=substation]; area({{bbox}})[power=substation];); out center;",
          "location": "$USER",
          "radius": 2500,
          "minRadius": 0,
          "bypass_commands": [
            {"#comment":"$ITEMS contains an array of results: type=way, lat, lon, tags. by convention center is copied down into lat/lon"},
            {"for_each":{"param":"$ITEMS"},"do":[
              {"set":{"param":"loc"},"value":{"create_array":[
                {"struct":{"param":"$item"},"path":"lat"},
                {"struct":{"param":"$item"},"path":"lon"}
              ]}},
              {"set_map":{"add":{"point":{"location":{"param":"loc"},"icon":"ki_helipad"}}}}
            ]}
          ]
        }},

        {"sleep": "forever"}
      ]
    }
  ]
}

# Train level crossing objects

This sample accesses the road and rail data to determine a good place to place a crash involving a train and a school bus.

  1. Find a nearby level train crossing
  2. Get the roads and rails connected to that crossing
  3. Place a train on the crossing, and a bus crashing into it from the direction of the road.
{
  "title": "Train Level Crossing Test",
  "api_version": 0.1,
  "aircraft": ["H145"],
  "locations":{
    "LOC":"$USER"
  },
  "briefing":[
    {"buttonbar":[ {"title":"teleport to accident","commands":[ {"call_macro":"adv_teleport","params":{ "loc": "accident_location" }} ] }   ]}
  ],
  "macros":{
    "adv_teleport":[
      {"#comment":"param: loc"},
      {"trigger":"K:SLEW_ON"},
      {"sleep":1},
      {"teleport_to":{"param":"loc"}},
      {"set":{"var":["A:PLANE ALTITUDE", "feet"]}, "value":-1000},
      {"set":{"var":["A:PLANE HEADING DEGREES TRUE", "degrees"]}, "value":{"param":"hdg"}},
      {"sleep":2},
      {"trigger":"K:SLEW_OFF"}
    ],
    "create_closest_railway_crossing_accident_scene":[
      {"#comment": "param - location"},
      
      {"#comment":"first get the closest result and get the node ID"},
      {"create_location": "accident_location", "zones": [
        {"zone": {
          "zone_type": "query_random_result",
          "radius":10000,
          "query": "[out:json];node({{bbox}})[railway=\"level_crossing\"];out center;",
          "location": {"param":"location"},
          "commands":[ {"set":{"param":"railway_crossing_node_id"}, "value":{"param":"$LOCATION:ID"}} ]
        }}
      ]},
      
      {"#comment":"query nearby highway and railway (very close because it's already the exact position). We get all the nodes in those ways."},
      {"osm_query_data": 
        "[out:json];(way({{bbox}})[railway]; way({{bbox}})[highway];);(._;>;);out;", 
        "location":"accident_location",
        "size": 1,
        "result":"my_data"
      },
      
      {"#comment":"find all the connected nodes and put them into highway and railway lists"},
      {"set":{"param":"highway_nodes"}, "value":{"create_array":[]}},
      {"set":{"param":"railway_nodes"}, "value":{"create_array":[]}},

      {"#comment":"get the nodes connected to our root (intersection) node. these are nodes before/after the root node, on any ways that the node is apart of"},
      {"osm_get_connected_nodes":{"param":"railway_crossing_node_id"}, "data": {"param":"my_data"}, "result":"my_nodes_connected_to_nearest_node"},
      {"for_each": {"param":"my_nodes_connected_to_nearest_node"}, "do":[
        {"#comment":"draw a line with a different color for each leg of the intersection"},
        
        {"set":{"param":"first_way_key"}, "value":{"struct":{"object:keys":{"struct":{"param":"$item"}, "path":"_ways"}}, "index": 0}},
        {"set":{"param":"is_road"}, "value":{"struct":{"param":"$item"}, "path":"_ways.{first_way_key}.tags.highway"}},
        {"#comment":{"text":"is_road: {0}","params":[ {"json:stringify":{"param":"is_road"}} ]}},
        
        {"if":{"param":"is_road"},"ne":null,"then":[
          {"modify_array":{"param":"highway_nodes"}, "append":{"create_array":[ {"struct":{"param":"$item"},"path":"lat"}, {"struct":{"param":"$item"},"path":"lon"} ]}}
        ], "else":[
          {"modify_array":{"param":"railway_nodes"}, "append":{"create_array":[ {"struct":{"param":"$item"},"path":"lat"}, {"struct":{"param":"$item"},"path":"lon"} ]}}
        ]}
      ]},

      {"return":{"create_struct":{
        "nodeId": {"param":"railway_crossing_node_id"},
        "highway_nodes": {"param":"highway_nodes"},
        "railway_nodes": {"param":"railway_nodes"},
        "data": {"param":"my_data"},
        "location": {"resolve_location": "accident_location"}
      }}}
    ]
  },
  "objectives": [
    {
      "title": "Initializing...",
      "commands": [

        {"#comment":"find the closest railway/road level crossing"},
        {"call_macro":"create_closest_railway_crossing_accident_scene","params":{
          "location": "$USER"
        }, "result":"crossing_ret"},
    
        {"#comment":"get the extracted data"},
        {"set":{"param":"railway_crossing_node_id"},"value":{"struct":{"param":"crossing_ret"}, "path":"nodeId"}},
        {"set":{"param":"railway_crossing_location"},"value":{"struct":{"param":"crossing_ret"}, "path":"location"}},
        {"set":{"param":"highway_nodes"},"value":{"struct":{"param":"crossing_ret"}, "path":"highway_nodes"}},
        {"set":{"param":"railway_nodes"},"value":{"struct":{"param":"crossing_ret"}, "path":"railway_nodes"}},

        {"#comment":"visualize the results by putting an icon at the crossing and drawing a line to the nearby highway/railway nodes"},
        {"set_map":{"add":{"point":{ "location":{"param":"railway_crossing_location"}, "icon":"ki_waypoint_blue"}}}},
        {"set_map":{"add":{"line":{ "points":{"create_array":[
          {"param":"railway_crossing_location"},
          {"struct":{"param":"highway_nodes"},"index":"0"}
        ]}, "stroke":{"create_struct":{ "color":"red", "width":2}}}}}},
        {"set_map":{"add":{"line":{ "points":{"create_array":[
          {"param":"railway_crossing_location"},
          {"struct":{"param":"railway_nodes"},"index":"0"}
        ]}, "stroke":{"create_struct":{ "color":"blue", "width":2}}}}}},

        {"#comment":"calculate bearings"},
        {"set":{"param":"train_brg"},"value":{"bearing":{"from":{"param":"railway_crossing_location"},"to":{"struct":{"param":"railway_nodes"},"index":"0"}}}},
        {"set":{"param":"crash_brg"},"value":{"bearing":{"from":{"param":"railway_crossing_location"},"to":{"struct":{"param":"highway_nodes"},"index":"0"}}}},

        {"#comment":"place train and crash objects"},
        {"create_object": {"name": "train", "title": "Airbus H145 Train", "location": {"bearing2":{"param":"train_brg"},"dist":20,"object": {"param":"railway_crossing_location"}}}},
        {"track_object": {"object": "train", "icon": "ki_helipad"}},

        {"create_object": {"name": "crash1", "title": "Airbus H145 School bus", "location": {"bearing2":{"param":"crash_brg"},"dist":-7,"object": {"param":"railway_crossing_location"}}}},
        {"track_object": {"object": "crash1", "icon": "ki_helipad"}},

        {"sleep": "forever"}
      ]
    }
  ]
}

# Road network test program

This sample will:

  1. Query a second of the road network
  2. Draw red lines on all of the roads
  3. Find the closest road intersection to LOC location.
  4. Draw a different color line from each leg of the intersection to the center.
{
  "title": "Road Network Test",
  "api_version": 0.1,
  "aircraft": ["H145"],
  "data": {
    "colors": ["hotpink","blue","orange","yellow","green","purple"]
  },
  "locations":{
    "LOC":{"bearing":270,"dist":500}
  },
  "objectives": [
    {
      "title": "Initializing...",
      "commands": [
        {"#comment":"Query a block of road network data and save it into my_data"},
        {"osm_query_data": 
          "[out:json];way({{bbox}})[highway~\"^(motorway|trunk|primary|secondary|unclassified|residential|living_street|service|tertiary|(motorway|trunk|primary|secondary|tertiary|)_link)$\"];(._;>;);out;", 
          "location":"LOC",
          "size": 600,
          "result":"my_data"
        },

        {"#comment":"extract the list of ways into my_ways, and then loop over them and draw them all on the map"},
        {"osm_get_all_ways": {"param":"my_data"}, "result":"my_ways"},
        {"for_each":{"param":"my_ways"},"do":[
          {"#comment":"create a list and then get a list of all the nodes in my_ways. store that list of nodes into my_nodes_on_way"},
          {"set":{"param":"node_location_list"}, "value":{"create_array":[]}},
          {"osm_get_nodes":{"struct":{"param":"$item"}, "path":"id"}, "data": {"param":"my_data"}, "result":"my_nodes_on_way"},
          {"#comment":"create a [lat,lon] array from each node and put it into the results list"},
          {"for_each": {"param":"my_nodes_on_way"}, "do":[
            {"modify_array":{"param":"node_location_list"}, "append":{"create_array":[
              {"struct":{"param":"$item"},"path":"lat"},
              {"struct":{"param":"$item"},"path":"lon"}
            ]}},
            {"sleep":0.001}
          ]},
          {"#comment":"draw the road"},
          {"set_map":{"add":{"line":{ "points":{"param":"node_location_list"}, "stroke":{"color":"red", "width":2}}}}}
        ]},

        {"#comment":"get an ordered list of all the nodes by their distance from LOC"},
        {"osm_get_closest_nodes": "LOC", "data": {"param":"my_data"}, "result":"my_closest_nodes"},

        {"#comment":"go through the result nodes and pick only those with at least 2 parents (an intersection)"},
        {"set":{"param":"closest_node"},"value":null},
        {"for_each":{"param":"my_closest_nodes"},"do":[
          {"osm_get_parent_ways":{"struct":{"param":"$item"},"path":"id"}, "data": {"param":"my_data"}, "result":"parents"},
          
          {"if":{"struct":{"param":"parents"}, "path":"length"},"gt": 1,"then":[
            {"set":{"param":"closest_node"},"value":{"param":"$item"}},
            {"break":1}
          ]},
          {"#comment":"do get parent and check for more than one for an intersection"},
          {"sleep":0.001}
        ]},
        {"set":{"param":"closest_location"}, "value":{"create_array":[
          {"struct": {"param":"closest_node"},"path":"lat"},
          {"struct": {"param":"closest_node"},"path":"lon"}
        ]}},
        
        {"set_map":{"add":{"point":{ "location":{"param":"closest_location"}, "icon":"ki_waypoint_blue"}}}},
        
        {"#comment":"get the nodes connected to our root (intersection) node. these are nodes before/after the root node, on any ways that the node is apart of"},
        {"osm_get_connected_nodes":{"struct":{"param":"closest_node"},"path":"id"}, "data": {"param":"my_data"}, "result":"my_nodes_connected_to_nearest_node"},
        {"for_each": {"param":"my_nodes_connected_to_nearest_node"}, "do":[
          {"#comment":"draw a line with a different color for each leg of the intersection"},
          {"set_map":{"add":{"line":{ "points":{"create_array":[
            {"param":"closest_location"},
            {"create_array":[
              {"struct":{"param":"$item"},"path":"lat"},
              {"struct":{"param":"$item"},"path":"lon"}
            ]}
          ]}, "stroke":{"create_struct":{
            "color":{"struct": {"static": "colors"}, "index": {"param":"$index"}}, "width":2}}}}}}
        ]},
        
        {"#comment":"save some debug stuff"},
        {"set":{"local":"node_location_list2"},"value":{"param":"node_location_list2"}},
        {"set":{"local":"my_closest_nodes"},"value":{"param":"my_closest_nodes"}},
        {"set":{"local":"my_ways"},"value":{"param":"my_ways"}},
        {"set":{"local":"my_data"},"value":{"param":"my_data"}},
        

        {"sleep": "forever"}
      ]
    }
  ]
}

# Water polygon test program

You must be essentially on top of a lake for this to work. Change LOC to be in the middle of a lake, or increase the size. You should find only one body of water or else the area message will be overwritten.

This sample:

  1. Queries nearby water polygons
  2. Draws a bunch of points on the map to determine if they are inside or outside of the water
{
  "title": "get very nearby water and get area",
  "api_version": 0.1,
  "aircraft": ["H145"],
  "locations":{
    "LOC":"$USER"
  },
  "objectives": [
    {
      "title": "Initializing...",
      "commands": [
        {"#comment":"Query a block of road network data and save it into my_data"},
        {"osm_query_data": 
          "[out:json];nwr({{bbox}})[natural=water];(._;>;);out;", 
          "location":"LOC",
          "size": 200,
          "result":"my_data"
        },

        {"#comment":"extract the list of ways into my_ways, and then loop over them and draw them all on the map"},
        {"osm_get_all_ways": {"param":"my_data"}, "result":"my_ways"},
        {"for_each":{"param":"my_ways"},"do":[
          {"set":{"param":"way"},"value": {"param":"$item"}},
          {"#comment":"create a list and then get a list of all the nodes in my_ways. store that list of nodes into my_nodes_on_way"},
          {"set":{"param":"node_location_list"}, "value":{"create_array":[]}},
          {"osm_get_nodes":{"struct":{"param":"$item"}, "path":"id"}, "data": {"param":"my_data"}, "result":"my_nodes_on_way"},
          {"#comment":"create a [lat,lon] array from each node and put it into the results list"},
          {"for_each": {"param":"my_nodes_on_way"}, "do":[
            {"modify_array":{"param":"node_location_list"}, "append":{"create_array":[
              {"struct":{"param":"$item"},"path":"lat"},
              {"struct":{"param":"$item"},"path":"lon"}
            ]}},
            {"sleep":0.001}
          ]},
          
          {"osm_get_area_of_area":{"struct":{"param":"way"},"path":"id"},  "data": {"param":"my_data"}, "result":"way_area"},  
          {"set_message":{"text":"way size {way_area} meters"}},
          
          {"#comment":"draw the road"},
    
          {"for_each":{"create_array": 10},"do":[
            {"set":{"param":"dist"},"value":{"multiply":[ {"param":"$index"}, 30 ]}},
            {"for_each":{"create_array": 36},"do":[
              {"set":{"param":"brg"},"value":{"multiply":[ {"param":"$index"}, 10 ]}},
              {"osm_is_point_within_way": {"struct":{"param":"way"}, "path":"id"}, "location":{"bearing":{"param":"brg"},"dist":{"param":"dist"}}, "data":{"param":"my_data"},"result":"is_in"},
          
              {"if":{"param":"is_in"}, "eq":1,"then":[
                {"set_map":{"add":{"point":{ "location":{"bearing":{"param":"brg"},"dist":{"param":"dist"}}, "icon":"ki_helipad"}}}}
              ],"else":[
                {"set_map":{"add":{"point":{ "location":{"bearing":{"param":"brg"},"dist":{"param":"dist"}}, "icon":"ki_waypoint_blue"}}}}
              ]},
              
              {"sleep":0.001}
            ]},
            {"sleep":0.001}
          ]},
          

          
          {"set_map":{"add":{"line":{ "points":{"param":"node_location_list"}, "stroke":{"color":"red", "width":2}}}}}
        ]},

        {"sleep": "forever"}
      ]
    }
  ]
}

# Buildings test

  1. Get the nearby buildings and outline them in red.
{
  "title": "Get nearby buildings and outline them",
  "api_version": 0.1,
  "aircraft": ["H145"],
  "locations":{
    "LOC":"$USER"
  },
  "objectives": [
    {
      "title": "Initializing...",
      "commands": [
        {"#comment":"Query a block of road network data and save it into my_data"},
        {"osm_query_data": 
          "[out:json];way({{bbox}})[building];(._;>;);out;", 
          "location":"LOC",
          "size": 100,
          "result":"my_data"
        },

        {"#comment":"extract the list of ways into my_ways, and then loop over them and draw them all on the map"},
        {"osm_get_all_ways": {"param":"my_data"}, "result":"my_ways"},
        {"for_each":{"param":"my_ways"},"do":[
          {"#comment":"create a list and then get a list of all the nodes in my_ways. store that list of nodes into my_nodes_on_way"},
          {"set":{"param":"node_location_list"}, "value":{"create_array":[]}},
          {"osm_get_nodes":{"struct":{"param":"$item"}, "path":"id"}, "data": {"param":"my_data"}, "result":"my_nodes_on_way"},
          {"#comment":"create a [lat,lon] array from each node and put it into the results list"},
          {"for_each": {"param":"my_nodes_on_way"}, "do":[
            {"modify_array":{"param":"node_location_list"}, "append":{"create_array":[
              {"struct":{"param":"$item"},"path":"lat"},
              {"struct":{"param":"$item"},"path":"lon"}
            ]}},
            {"sleep":0.001}
          ]},
          {"#comment":"draw the building"},
          {"set_map":{"add":{"line":{ "points":{"param":"node_location_list"}, "stroke":{"color":"red", "width":2}}}}}
        ]},

        {"sleep": "forever"}
      ]
    }
  ]
}