Illustrates the use of a secondary Giro3D instance to display a minimap.

Giro3D version
THREE.js version
OpenLayers version
CRS
Memory usage (CPU)
Memory usage (GPU)
Frames
Clear color
Clear alpha
Status
Local clipping enabled
Capabilities
WebGL 2
Max texture units
Max texture size
Precision
Max fragment shader uniforms
Logarithmic depth buffer
Max shader attributes
Check shader errors
EXT_clip_control
EXT_color_buffer_float
EXT_color_buffer_half_float
EXT_conservative_depth
EXT_depth_clamp
EXT_float_blend
EXT_polygon_offset_clamp
EXT_texture_compression_bptc
EXT_texture_compression_rgtc
EXT_texture_filter_anisotropic
EXT_texture_mirror_clamp_to_edge
EXT_texture_norm16
NV_shader_noperspective_interpolation
OES_draw_buffers_indexed
OES_sample_variables
OES_shader_multisample_interpolation
OES_texture_float_linear
OVR_multiview2
WEBGL_clip_cull_distance
WEBGL_compressed_texture_astc
WEBGL_compressed_texture_etc
WEBGL_compressed_texture_etc1
WEBGL_compressed_texture_s3tc
WEBGL_compressed_texture_s3tc_srgb
WEBGL_debug_renderer_info
WEBGL_debug_shaders
WEBGL_lose_context
WEBGL_multi_draw
WEBGL_polygon_mode
WEBGL_stencil_texturing
MSAA
EDL
EDL Radius
EDL Strength
Inpainting
Inpainting steps
Inpainting depth contrib.
Point cloud occlusion
Type
FOV
Automatic plane computation
Far plane
Near plane
Max far plane
Min near plane
Width (pixels)
Height (pixels)
x
y
z
x
y
z
color
Enable cache
Default TTL (seconds)
Capacity (MB)
Capacity (entries)
Entries
Memory usage (approx)
Pending requests
Memory tracker
Identifier
Memory usage (CPU)
Memory usage (GPU)
Status
Render order
Enable
Plane normal X
Plane normal Y
Plane normal Z
Distance
Helper size
Negate plane
Visible
Freeze updates
Opacity
Show volumes
Volume color
Discard no-data values
Sidedness
Front
Depth test
Visible tiles
Reachable tiles
Loaded tiles
Cast shadow
Receive shadow
Tile width (pixels)
Tile height (pixels)
Show grid
Background
Background opacity
Show tiles outlines
Tile outline color
Show tile info
Show extent
Extent color
Subdivision threshold
Deformation
Wireframe
Tile subdivisions
Show collider meshes
CPU terrain
Stitching
Geometry pool
Enabled
Mode
Hillshade
Hillshade intensity
Z factor
Hillshade zenith
Hillshade azimuth
Elevation layers only
Enable
Color
Opacity
X step
Y step
X Offset
Y Offset
Thickness
Enable
Color
Thickness
Opacity
Primary interval (m)
Secondary interval (m)
Brightness
Contrast
Saturation
Layer count
Render state
Normal
Layers
Show helpers
Show hidden objects
Name filter
Hierarchy
Properties
isObject3D
uuid
name
type
matrixAutoUpdate
matrixWorldAutoUpdate
matrixWorldNeedsUpdate
visible
castShadow
receiveShadow
frustumCulled
renderOrder
x
y
z
x
y
z
Giro3D version
THREE.js version
OpenLayers version
CRS
Memory usage (CPU)
Memory usage (GPU)
Frames
Clear color
Clear alpha
Status
Local clipping enabled
Capabilities
WebGL 2
Max texture units
Max texture size
Precision
Max fragment shader uniforms
Logarithmic depth buffer
Max shader attributes
Check shader errors
EXT_clip_control
EXT_color_buffer_float
EXT_color_buffer_half_float
EXT_conservative_depth
EXT_depth_clamp
EXT_float_blend
EXT_polygon_offset_clamp
EXT_texture_compression_bptc
EXT_texture_compression_rgtc
EXT_texture_filter_anisotropic
EXT_texture_mirror_clamp_to_edge
EXT_texture_norm16
NV_shader_noperspective_interpolation
OES_draw_buffers_indexed
OES_sample_variables
OES_shader_multisample_interpolation
OES_texture_float_linear
OVR_multiview2
WEBGL_clip_cull_distance
WEBGL_compressed_texture_astc
WEBGL_compressed_texture_etc
WEBGL_compressed_texture_etc1
WEBGL_compressed_texture_s3tc
WEBGL_compressed_texture_s3tc_srgb
WEBGL_debug_renderer_info
WEBGL_debug_shaders
WEBGL_lose_context
WEBGL_multi_draw
WEBGL_polygon_mode
WEBGL_stencil_texturing
MSAA
EDL
EDL Radius
EDL Strength
Inpainting
Inpainting steps
Inpainting depth contrib.
Point cloud occlusion
Type
Automatic plane computation
Far plane
Near plane
Max far plane
Min near plane
Width (pixels)
Height (pixels)
x
y
z
color
Enable cache
Default TTL (seconds)
Capacity (MB)
Capacity (entries)
Entries
Memory usage (approx)
Pending requests
Memory tracker
Identifier
Memory usage (CPU)
Memory usage (GPU)
Status
Render order
Enable
Plane normal X
Plane normal Y
Plane normal Z
Distance
Helper size
Negate plane
Visible
Freeze updates
Opacity
Show volumes
Volume color
Discard no-data values
Sidedness
Front
Depth test
Visible tiles
Reachable tiles
Loaded tiles
Cast shadow
Receive shadow
Tile width (pixels)
Tile height (pixels)
Show grid
Background
Background opacity
Show tiles outlines
Tile outline color
Show tile info
Show extent
Extent color
Subdivision threshold
Deformation
Wireframe
Tile subdivisions
Show collider meshes
CPU terrain
Stitching
Geometry pool
Enabled
Mode
Hillshade
Hillshade intensity
Z factor
Hillshade zenith
Hillshade azimuth
Elevation layers only
Enable
Color
Opacity
X step
Y step
X Offset
Y Offset
Thickness
Enable
Color
Thickness
Opacity
Primary interval (m)
Secondary interval (m)
Brightness
Contrast
Saturation
Layer count
Render state
Normal
Layers
Identifier
Memory usage (CPU)
Memory usage (GPU)
Name
Source CRS
Status
Resolution factor
Visible
Frozen
Interpretation
Loaded images
Blending mode
Normal
Brightness
Contrast
Saturation
Opacity
Show extent
Extent color
Enabled
Mode
Elevation
Lower bound
Upper bound
Type
Color space
Data type
Flip Y
Synchronous
CRS
Memory usage (CPU)
Memory usage (GPU)
Loaded/Requested
CRS
Zoom levels
Main URL
Inner source
Show helpers
Show hidden objects
Name filter
Hierarchy
Properties
isObject3D
uuid
name
type
matrixAutoUpdate
matrixWorldAutoUpdate
matrixWorldNeedsUpdate
visible
castShadow
receiveShadow
frustumCulled
renderOrder
x
y
z
x
y
z
100% © IGN

You can use any number of Giro3D instance in a given page. One common use for multiple instances is to implement a minimap view, where the minimap view contains a single map with a simple, fast to load color layer. You can also synchronize the position of the minimap camera with the main view camera. In this example, the minimap view uses an orthographic camera.

index.js
import {
  Vector3,
  CubeTextureLoader,
  Color,
  OrthographicCamera,
  MathUtils,
} from "three";
import { MapControls } from "three/examples/jsm/controls/MapControls.js";

import OSM from "ol/source/OSM.js";

import Coordinates from "@giro3d/giro3d/core/geographic/Coordinates.js";
import Instance from "@giro3d/giro3d/core/Instance.js";
import Extent from "@giro3d/giro3d/core/geographic/Extent.js";
import WmtsSource from "@giro3d/giro3d/sources/WmtsSource.js";
import ColorLayer from "@giro3d/giro3d/core/layer/ColorLayer.js";
import ElevationLayer from "@giro3d/giro3d/core/layer/ElevationLayer.js";
import Map from "@giro3d/giro3d/entities/Map.js";
import Inspector from "@giro3d/giro3d/gui/Inspector.js";
import BilFormat from "@giro3d/giro3d/formats/BilFormat.js";
import TiledImageSource from "@giro3d/giro3d/sources/TiledImageSource.js";

Instance.registerCRS(
  "EPSG:2154",
  "+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs",
);
Instance.registerCRS(
  "IGNF:WGS84G",
  'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]',
);

const SKY_COLOR = new Color(0xf1e9c6);

const mainInstance = new Instance({
  target: "view",
  crs: "EPSG:2154",
  backgroundColor: SKY_COLOR,
});

// create a map
const extent = new Extent(
  "EPSG:2154",
  -111629.52,
  1275028.84,
  5976033.79,
  7230161.64,
);
const map = new Map({
  extent,
  backgroundColor: "gray",
  lighting: {
    enabled: true,
    elevationLayersOnly: true,
  },
  discardNoData: true,
});
mainInstance.add(map);

const noDataValue = -1000;

const capabilitiesUrl =
  "https://data.geopf.fr/wmts?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetCapabilities";

WmtsSource.fromCapabilities(capabilitiesUrl, {
  layer: "ELEVATION.ELEVATIONGRIDCOVERAGE.HIGHRES",
  format: new BilFormat(),
  noDataValue,
})
  .then((elevationWmts) => {
    map.addLayer(
      new ElevationLayer({
        name: "wmts_elevation",
        extent: map.extent,
        // We don't need the full resolution of terrain because we are not using any shading
        resolutionFactor: 0.25,
        minmax: { min: 0, max: 5000 },
        noDataOptions: {
          replaceNoData: false,
        },
        source: elevationWmts,
      }),
    );
  })
  .catch(console.error);

WmtsSource.fromCapabilities(capabilitiesUrl, {
  layer: "HR.ORTHOIMAGERY.ORTHOPHOTOS",
})
  .then((orthophotoWmts) => {
    map.addLayer(
      new ColorLayer({
        name: "wmts_orthophotos",
        extent: map.extent,
        source: orthophotoWmts,
      }),
    );
  })
  .catch(console.error);

mainInstance.view.camera.position.set(
  913349.2364044407,
  6456426.459171033,
  1706.0108044011636,
);
const lookAt = new Vector3(913896, 6459191, 200);
mainInstance.view.camera.lookAt(lookAt);
mainInstance.notifyChange(mainInstance.view.camera);

const controls = new MapControls(
  mainInstance.view.camera,
  mainInstance.domElement,
);
controls.enableDamping = true;
controls.dampingFactor = 0.2;
controls.target.copy(lookAt);
controls.saveState();
mainInstance.view.setControls(controls);

const cubeTextureLoader = new CubeTextureLoader();
cubeTextureLoader.setPath("image/skyboxsun25deg_zup/");
const cubeTexture = cubeTextureLoader.load([
  "px.jpg",
  "nx.jpg",
  "py.jpg",
  "ny.jpg",
  "pz.jpg",
  "nz.jpg",
]);

mainInstance.scene.background = cubeTexture;

/////////////////////////////// Minimap configuration //////////////////////////////////////////////

// The minimap is a regular Giro3D instance located in a <div> element in the top right corner
// of the window. Its configuration is very similar to the main view, with some important
// differences related to the camera and navigation.

// Create our minimap instance and attach it to the 'minimap' <div> element.
const minimapInstance = new Instance({
  target: "minimap",
  crs: "EPSG:3857", // Contrary to the main view, this minimap uses the Web mercator projection
});

// Set the minimap camera view width, in meters. This can be changed later when
// the user uses the mouse wheel on to zoom in/out.
const minimapCameraWidth = 2000;
const minimapCamera = new OrthographicCamera(
  -minimapCameraWidth / 2,
  minimapCameraWidth / 2,
  100,
  -100,
);

// We replace the default perspective camera of the minimap view by
// an orthographic camera, which is much more suitable for this kind of view.
minimapInstance.view.camera = minimapCamera;

// Let's create our minimap map with the same extent than the main map.
const minimap = new Map({
  extent: map.extent.as(minimapInstance.referenceCrs),
  // Since the map is flat (no terrain applied), we can use 1 segment per tile.
  segments: 1,
  backgroundColor: "black",
  terrain: false, // We can disable terrain because our map will be flat.
});

minimapInstance.add(minimap);

// We use an OpenStreetMap color layer for the minimap, because it's readable and fast to display.
const osmLayer = new ColorLayer({
  name: "osm",
  preloadImages: true,
  source: new TiledImageSource({ source: new OSM() }),
});

minimap.addLayer(osmLayer);

function synchronizeCameras() {
  const target = controls.target;

  // Since our minimap does not use the same projection as the main view (EPSG:2154),
  // we must convert the camera position into this projection (EPSG:3857).
  const srcProj = mainInstance.referenceCrs;
  const dstProj = minimapInstance.referenceCrs;
  const srcPosition = new Coordinates(srcProj, target.x, target.y);
  const position = srcPosition.as(dstProj);

  // Then we can assign the position to the camera, while still keeping a constant altitude.
  // The minimap camera "altitude" never changes because we are using an orthographic camera.
  // Changing this value will not change the size of objects rendered in this view.
  const MINIMAP_CAMERA_ALTITUDE = 10;
  minimapInstance.view.camera.position.set(
    position.x,
    position.y,
    MINIMAP_CAMERA_ALTITUDE,
  );

  // Instruct the minimap instance to render the view.
  minimapInstance.notifyChange(minimap);
}

// Synchronize the minimap camera position with the *target* of the main camera
mainInstance.addEventListener("after-camera-update", synchronizeCameras);

function handleMouseWheel(event) {
  const delta = event.wheelDelta;

  const absDelta = Math.abs(delta);
  const ZOOM_SPEED = MathUtils.mapLinear(absDelta, 0, 120, 1.01, 1.2);

  if (delta > 0) {
    minimapCamera.zoom *= ZOOM_SPEED;
  } else if (delta < 0) {
    minimapCamera.zoom /= ZOOM_SPEED;
  }

  minimapInstance.notifyChange(minimapCamera);

  // Prevent this event from bubbling and make the page scroll.
  event.preventDefault();
}

// Use the mouse wheel on the minimap view to set the zoom of the minimap camera.
// We use the 'wheel' event (careful not to use the non-standard 'mousewheel' event).
minimapInstance.domElement.addEventListener("wheel", handleMouseWheel);

Inspector.attach("inspector", mainInstance, { title: "main" });
Inspector.attach("inspector", minimapInstance, { title: "minimap" });
index.html
<!doctype html>
<html lang="en">
  <head>
    <title>Minimap</title>
    <meta charset="UTF-8" />
    <meta name="name" content="minimap" />
    <meta
      name="description"
      content="Illustrates the use of a secondary Giro3D instance to display a minimap."
    />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link rel="icon" href="https://giro3d.org/images/favicon.svg" />
    <link
      href="https://giro3d.org/assets/bootstrap-custom.css"
      rel="stylesheet"
    />
    <script src="https://giro3d.org/assets/bootstrap.bundle.min.js"></script>
    <link
      rel="stylesheet"
      type="text/css"
      href="https://giro3d.org/latest/examples/css/example.css"
    />
  </head>

  <body>
    <div id="view" class="m-0 p-0 w-100 h-100"></div>
    <div
      id="inspector"
      class="position-absolute top-0 start-0 mh-100 overflow-auto"
    ></div>

    <div class="side-pane-with-status-bar">
      <div
        id="minimap"
        class="card m-1"
        style="
          width: 20rem;
          height: 14rem;
          overflow: hidden;
          border-radius: var(--bs-card-border-radius);
        "
      ></div>
    </div>

    <script type="module" src="index.js"></script>
    <script>
      /* activate popovers */
      const popoverTriggerList = [].slice.call(
        document.querySelectorAll('[data-bs-toggle="popover"]'),
      );
      popoverTriggerList.map(
        // bootstrap is used as script in the template, disable warning about undef
        // eslint-disable-next-line no-undef
        (popoverTriggerEl) =>
          new bootstrap.Popover(popoverTriggerEl, {
            trigger: "hover",
            placement: "left",
            content: document.getElementById(
              popoverTriggerEl.getAttribute("data-bs-content"),
            ).innerHTML,
            html: true,
          }),
      );
    </script>
  </body>
</html>
package.json
{
    "name": "minimap",
    "dependencies": {
        "@giro3d/giro3d": "0.42.3"
    },
    "devDependencies": {
        "vite": "^3.2.3"
    },
    "scripts": {
        "start": "vite",
        "build": "vite build"
    }
}