- [API Reference - `map`](#api-reference---map)
  - [Spawning objects](#spawning-objects)
    - [`map.spawn(object_definition)`](#mapspawnobject_definition)
    - [`map.spawn_uav(robot_definition)`](#mapspawn_uavrobot_definition)
    - [`map.spawn_ugv(robot_definition)`](#mapspawn_ugvrobot_definition)
    - [`map.spawn_urdf(urdf_robot_definition)`](#mapspawn_urdfurdf_robot_definition)
    - [`map.spawn_goal(goal_definition)`](#mapspawn_goalgoal_definition)
    - [`map.spawn_road(road_definition)`](#mapspawn_roadroad_definition)
    - [`map.spawn_camera(camera_definition)`](#mapspawn_cameracamera_definition)
  - [Working with GPS coordinates](#working-with-gps-coordinates)
    - [`map.gps_to_enu(gps_coords)`](#mapgps_to_enugps_coords)
    - [`map.enu_to_gps(enu_coords)`](#mapenu_to_gpsenu_coords)
    - [`map.set_zero(gps_coords)`](#mapset_zerogps_coords)
  - [Spatial queries](#spatial-queries)
    - [`map.find_all(params)`](#mapfind_allparams)
    - [`map.find_nearest(params)`](#mapfind_nearestparams)
    - [`map.find_in_aabb(params)`](#mapfind_in_aabbparams)
    - [`map.intersection_with_point(params)`](#mapintersection_with_pointparams)
    - [`map.intersection_with_sphere(params)`](#mapintersection_with_sphereparams)
    - [`map.intersection_with_ray(params)`](#mapintersection_with_rayparams)

## API Reference - `map`

This module allows you to create a map, spawn objects or vehicles, and perform map queries (get closest object, get objects in the area, etc.).

### Spawning objects

#### `map.spawn(object_definition)`

Spawns GLTF object on the map. Currently, all GLTF objects are loaded from `assets` directory and need to have all meshes to be a named a certain way.

List of available objects that come with the simulator:
 - `buildings/building1.glb`
 - `buildings/building2.glb`
 - `buildings/building31.glb`
 - `buildings/building32.glb`
 - `trees/tree_a.glb`
 - `trees/tree_b.glb`

Object definition is a table with the following fields:
 - `mesh` (string) - path to the GLTF object (see above)
 - `position` (vec3, optional) - position of the object on the map, in local coordinates (X, Y, Z)
 - `rotation` (bivec3, optional) - rotation of the object on the map, roll-pitch-yaw (YZ, ZX, XY)
 - `scale` (f32, optional) - scale of the object

```lua
map.spawn({
    mesh = "trees/tree_a.glb",
    position = { x = 1.5, y = 0 },
    rotation = { yz = 0.1, zx = 0, xy = 0 },
})
```

```js
await client.rpc('map.spawn', {
    mesh: 'trees/tree_a.glb',
    position: { x: 1.5, y: 0 },
    rotation: { yz: 0.1, zx: 0, xy: 0 },
})
```

#### `map.spawn_uav(robot_definition)`

Spawns aerial vehicle on the map.

Robot definition is a table with the following fields:
 - `robot_id` (u32) - unique identifier of the robot
 - `position` (vec3, optional) - position of the robot on the map, in local coordinates (X, Y, Z)
 - `rotation` (bivec3, optional) - rotation of the robot on the map, roll-pitch-yaw (YZ, ZX, XY)

```lua
map.spawn_uav({
    robot_id = 1,
    position = { x = 0, y = 1 },
    rotation = { yz = 0, zx = 0, xy = 0 },
})
```

```js
await client.rpc('map.spawn_uav', {
    robot_id: 1,
    position: { x: 0, y: 1 },
    rotation: { yz: 0, zx: 0, xy: 0 },
})
```

#### `map.spawn_ugv(robot_definition)`

Spawns ground vehicle on the map. See `map.spawn_uav` for details about robot definition.

```lua
map.spawn_ugv({
    robot_id = 2,
    position = { x = 0, y = -1 },
    rotation = { yz = 0, zx = 0, xy = 0 },
})
```

```js
await client.rpc('map.spawn_ugv', {
    robot_id: 2,
    position: { x: 0, y: -1 },
    rotation: { yz: 0, zx: 0, xy: 0 },
})
```

#### `map.spawn_urdf(urdf_robot_definition)`

Loads robot from URDF file and spawns it on the map.

URDF Robot definition is a table with the following fields:
 - `robot_id` (u32) - unique identifier of the robot
 - `position` (vec3, optional) - position of the robot on the map, in local coordinates (X, Y, Z)
 - `rotation` (bivec3, optional) - rotation of the robot on the map, roll-pitch-yaw (YZ, ZX, XY)
 - `urdf_path` (string) - path to the URDF file
 - `mesh_dir` (string) - path to the directory with STL files

```lua
map.spawn_urdf({
    robot_id = 3,
    position = { x = 10, y = 0 },
    rotation = { yz = 0, zx = 0, xy = 0 },
    urdf_path = "flamingo_edu/urdf/Edu_v4.urdf",
    mesh_dir = "assets/flamingo_edu/urdf",
})
```

```js
await client.rpc('map.spawn_urdf', {
    robot_id: 3,
    position: { x: 10, y: 0 },
    rotation: { yz: 0, zx: 0, xy: 0 },
    urdf_path: 'flamingo_edu/urdf/Edu_v4.urdf',
    mesh_dir: 'assets/flamingo_edu/urdf',
})
```

#### `map.spawn_goal(goal_definition)`

Spawns a goal (visual marker on the map) that toggles its state when a robot enters its vicinity.

Goal definition is a table with the following fields:
 - `position` (vec3, optional) - position of the goal on the map, in local coordinates (X, Y, Z)
 - `rotation` (bivec3, optional) - rotation of the goal on the map, roll-pitch-yaw (YZ, ZX, XY)
 - `radius` (f32, optional) - radius of the goal (default: 1)
 - `height` (f32, optional) - height of the goal (default: 2)
 - `active` (bool, optional) - initial state of the goal (active or not, default: true)

```lua
map.spawn_goal({
    position = { x = 0, y = 0, z = 0 },
})
```

```js
await client.rpc('map.spawn_goal', {
    position: { x: 0, y: 0, z: 0 },
})
```

#### `map.spawn_road(road_definition)`

Adds a road segment to the road network.

Road definition is a table with the following fields:
 - `src` (vec2) - start point of the road segment, in local coordinates (X, Y)
 - `dst` (vec2) - end point of the road segment, in local coordinates (X, Y)
 - `highway` (bool, optional) - whether the road is a highway (currently unused)

```lua
map.spawn_road({
    src = { x = 0, y = 0 },
    dst = { x = 10, y = 10 },
})
```

```js
await client.rpc('map.spawn_road', {
    src: { x: 0, y: 0 },
    dst: { x: 10, y: 10 },
})
```

#### `map.spawn_camera(camera_definition)`

Creates a camera object in the simulator.

Camera definition is a table with the following fields:
 - `position` (vec3, optional) - position of the camera on the map, in local coordinates (X, Y, Z)
 - `rotation` (bivec3, optional) - rotation of the camera on the map, roll-pitch-yaw (YZ, ZX, XY)
 - `size` (vec2, optional) - size of the camera image (X, Y, in pixels, default: `768x576`)
 - `fov` (f32, optional) - field of view of the camera in radians (default: `PI/4`)
 - `format` (string, optional) - mime-type of the received images (default: `image/tiff`)
 - `parent` (string, optional) - ID of the parent object to attach the camera to, static camera if not set
 - `depth_camera` (bool, optional) - make this camera a depth camera instead of rgb

Camera can be attached to a vehicle (moving with it) or be static. To attach a camera to a vehicle, set `parent` field to that vehicle's ID.

Format is a mime-type corresponding to the image format. Currently supported formats are:
  - `image/tiff` (default)
  - `image/png`
  - `image/jpeg`
  - `image/webp`
  - `image/bmp`

Note that formats like PNG and JPEG are slow to encode (up to a second for large sizes), that's why TIFF is chosen as the default. If you're getting just one image, PNG is fine, but if you want images faster than 1Hz, consider using TIFF.

You can switch camera to show depth instead of RGB image using `depth_camera` field. In this mode, the camera outputs pixel data where the hue and brightness represent the distance from camera to the object (red/bright is close, blue/dark is far). This mode is not yet configurable and is considered to be work-in-progress.

```lua
map.spawn_camera({
    position = { x = 0, y = 0, z = 0 },
    rotation = { yz = 0, zx = 0, xy = 0 },
    size = { w = 768, h = 576 },
    parent = "33v1",
    fov = math.pi / 4,
    format = "image/png",
})
```

```js
let camera_rgb = await client.rpc('map.spawn_camera', {
    position: { x: 0, y: 0, z: 0 },
    rotation: { yz: 0, zx: 0, xy: 0 },
    size: { w: 768, h: 576 },
    parent: '33v1',
    fov: Math.PI / 4,
    format: 'image/png',
})
```

### Working with GPS coordinates

This module allows you to convert between global (GPS) and local (ENU) coordinates, as well as set GPS origin for local coordinates.

#### `map.gps_to_enu(gps_coords)`

Converts GPS coordinates to local ENU coordinates.

```lua
local enu_coords = map.gps_to_enu({ lat = 50.844968, lon = 4.363814, alt = 0 })
```

```js
let enu_coords = await client.rpc('map.gps_to_enu', { lat: 50.844968, lon: 4.363814, alt: 0 })
```

Return value is a table with three fields - `x` (f32), `y` (f32), `z` (f32).

#### `map.enu_to_gps(enu_coords)`

Converts local ENU coordinates to GPS coordinates.

```lua
local gps_coords = map.enu_to_gps({ x = 1, y = 2, z = 3 })
```

```js
let gps_coords = await client.rpc('map.enu_to_gps', { x: 1, y: 2, z: 3 })
```

Return value is a table with three fields - `lat` (f32), `lon` (f32), `alt` (f32).

#### `map.set_zero(gps_coords)`

Sets GPS origin for local ENU coordinates.

```lua
map.set_zero({ lat = 50.844968, lon = 4.363814, alt = 0 })
```

```js
await client.rpc('map.set_zero', { lat: 50.844968, lon: 4.363814, alt: 0 })
```

### Spatial queries

This module allows you to perform spatial queries on the map, such as finding nearest object, finding objects in a certain area, or performing raycasts.

#### `map.find_all(params)`

Finds and returns all physical objects on the map.

Parameters is a table with the following fields:
 - `groups` (string\[\]) - list of object groups to include (default: all)

`groups` is a list of object groups to include in the search. If not set, all objects are included. Currently available groups are: `terrain`, `obstacle`, `vehicle`, `goal`.

```lua
local objects = map.find_all({
    groups = { "terrain", "obstacle" },
})
```

```js
let objects = await client.rpc('map.find_all', {
    groups: ['terrain', 'obstacle'],
})
```

Returns a list of objects. Each result is a table with the following fields:
 - `entity` (string) - ID of the object
 - `group` (string) - group of the object

#### `map.find_nearest(params)`

Finds the nearest object to a given point.

Parameters is a table with the following fields:
 - `point` (vec3) - point to check
 - `solid` (bool) - whether objects are considered hollow or solid (default: false)
 - `groups` (string\[\]) - list of object groups to include (default: all), see `map.find_all` for details
 - `exclude` (string\[\]) - list of object IDs to exclude from search
 - `anchor` (objectid) - use this object as a reference point instead of world center

If `solid` is set to `true`, all collider shapes are considered to be solid (so all queries and raycasts originating from inside of a solid shape will return the point itself). If it is set to `false`, all collider shapes are considered to be hollow (so all queries and raycasts originating from inside of a hollow shape will stop at that shape's boundary).

```lua
local nearest = map.find_nearest({
    point = { x = 0, y = 0, z = 10 },
    groups = { "terrain", "obstacle" },
})
```

```js
let nearest = await client.rpc('map.find_nearest', {
    point: { x: 0, y: 0, z: 10 },
    groups: ['terrain', 'obstacle'],
})
```

Returns a table with the following fields:
 - `entity` (string) - ID of the nearest object
 - `group` (string) - group of the object
 - `point` (vec3) - coordinates of the nearest point
 - `is_inside` (bool) - whether the point is inside the object

Example:

```lua
{
    entity = "11v1",
    group = "terrain",
    point = { x = 0, y = 0, z = 0 },
    is_inside = false,
}
```

Note: this call only returns one nearest object. If you want to get multiple objects (e.g. nearest 3), you can run the following script (inefficient for large number of objects):

```js
await client.rpc("script.eval", {
    code: `
        local result = {}
        local exclude = {}
        for idx = 1, ARGS.k do
            local found = sim.map.find_nearest {
                point = ARGS.point,
                exclude = exclude,
            }
            if found == nil then
                break
            end
            table.insert(result, found)
            table.insert(exclude, found.entity)
        end
        return result
    `,
    args: { point: { x: 0, y: 0, z: 0 }, k: 3 },
})
```

#### `map.find_in_aabb(params)`

Finds all objects intersecting with a given AABB (axis-aligned bounding box).

Parameters is a table with the following fields:
 - `center` (vec3) - center of the AABB
 - `half_extents` (vec3) - half extents of the AABB
 - `groups` (string\[\]) - list of object groups to include (default: all), see `map.find_all` for details
 - `anchor` (objectid) - use this object as a reference point instead of world center

```lua
local intersections = map.find_in_aabb({
    center = { x = 0, y = 0, z = 0 },
    half_extents = { x = 1, y = 1, z = 1 },
})
```

```js
let intersections = await client.rpc('map.find_in_aabb', {
    center: { x: 0, y: 0, z: 0 },
    half_extents: { x: 1, y: 1, z: 1 },
})
```

Returns a list of objects inside AABB. Each result is a table with the following fields:
 - `entity` (string) - ID of the object
 - `group` (string) - group of the object

Example:

```lua
{
    {
        entity = "11v1",
        group = "terrain",
    },
    {
        entity = "22v1",
        group = "obstacle",
    },
}
```

The example above would return all objects that touch the AABB (which in this case is a cube located at the origin with size 2x2x2).

Note: this call only returns object ids. If you want more information (e.g. position), you can use a custom script that iterates over the list of objects. Example:

```js
await client.rpc("script.eval", {
    code: `
        local found = sim.map.find_in_aabb {
            center = ARGS.center,
            half_extents = ARGS.half_extents,
        }
        local result = {}
        for idx = 1, #found do
            table.insert(result, {
                id = found[idx].entity,
                position = sim.object.position(found[idx].entity),
            })
        end
        return result
    `,
    args: {
        center: { x: 0, y: 0, z: 0 },
        half_extents: { x: 100, y: 100, z: 100 },
    },
})
```

#### `map.intersection_with_point(params)`

Finds all objects that contain a given point.

Parameters is a table with the following fields:
 - `point` (vec3) - point to check
 - `groups` (string\[\]) - list of object groups to include (default: all), see `map.find_all` for details
 - `exclude` (string\[\]) - list of object IDs to exclude from search
 - `limit` (u32) - maximum number of objects to return
 - `anchor` (objectid) - use this object as a reference point instead of world center

```lua
local intersections = map.intersection_with_point({
    point = { x = 0, y = 0, z = -1 },
    groups = { "terrain", "obstacle" },
})
```

```js
let intersections = await client.rpc('map.intersection_with_point', {
    point: { x: 0, y: 0, z: -1 },
    groups: ['terrain', 'obstacle'],
})
```

Returns a list of objects that contain the point. Each result is a table with the following fields:
 - `entity` (string) - ID of the object
 - `group` (string) - group of the object

Example:

```lua
{
    {
        entity = "11v1",
        group = "terrain",
    },
}
```

This particular example would usually return terrain (because negative altitude is set).

#### `map.intersection_with_sphere(params)`

Finds all objects within a given sphere.

Parameters is a table with the following fields:
 - `center` (vec3) - center of the sphere
 - `radius` (f32) - radius of the sphere
 - `groups` (string\[\]) - list of object groups to include (default: all), see `map.find_all` for details
 - `exclude` (string\[\]) - list of object IDs to exclude from search
 - `limit` (u32) - maximum number of objects to return
 - `anchor` (objectid) - use this object as a reference point instead of world center

```lua
local intersections = map.intersection_with_sphere({
    center = { x = 0, y = 0, z = 0 },
    radius = 1,
    groups = { "terrain", "obstacle" },
})
```

```js
let intersections = await client.rpc('map.intersection_with_sphere', {
    center: { x: 0, y: 0, z: 0 },
    radius: 1,
    groups: ['terrain', 'obstacle'],
})
```

Returns a list of objects that intersect with the sphere. Each result is a table with the following fields:
 - `entity` (string) - ID of the object
 - `group` (string) - group of the object

#### `map.intersection_with_ray(params)`

Finds all objects intersecting with a given ray, ordered by distance from the ray origin.

Parameters is a table with the following fields:
 - `ray_origin` (vec3) - origin of the ray
 - `ray_dir` (vec3) - direction of the ray
 - `max_toi` (f32) - maximum time of intersection (default: unlimited)
 - `solid` (bool) - whether objects are considered hollow or solid (default: false), see `map.find_nearest` for details
 - `groups` (string\[\]) - list of object groups to include (default: all), see `map.find_nearest` for details
 - `exclude` (string\[\]) - list of object IDs to exclude from search
 - `limit` (u32) - maximum number of objects to return
 - `anchor` (objectid) - use this object as a reference point instead of world center

```lua
local intersections = map.intersection_with_ray({
    ray_origin = { x = 0, y = 0, z = 10 },
    ray_dir = { x = 0, y = 0, z = -1 },
    groups = { "terrain", "obstacle", "vehicle" },
})
```

```js
let intersections = await client.rpc('map.intersection_with_ray', {
    ray_origin: { x: 0, y: 0, z: 10 },
    ray_dir: { x: 0, y: 0, z: -1 },
    groups: ['terrain', 'obstacle', 'vehicle'],
})
```

Returns a list of objects that intersect with the ray. Each result is a table with the following fields:
 - `entity` (string) - ID of the object
 - `group` (string) - group of the object
 - `toi` (f32) - time of impact of the ray with the object
 - `normal` (vec3) - normal at the intersection point

Contact point is not given, but you can compute it using `ray_origin + ray_dir * toi`.

Example:

```lua
{
    {
        entity = "11v1",
        group = "terrain",
        toi = 10,
        normal = { x = -0, y = -0, z = 1 },
    },
    {
        entity = "33v1",
        group = "vehicle",
        toi = 8.972,
        normal = { x = 5.8267085e-7, y = 0.0000069437583, z = 1 },
    },
}
```
