Skip to content

Streetlevel Modules Explained

Martin Raifer edited this page Aug 24, 2024 · 2 revisions

This page is intended to explain how the various street level services and layers are implemented.

The current street level providers implemented are:

  • Streetside
  • Mapillary
  • Kartaview
  • Mapilio
  • Vegbilder (regional, only in Norway)
  • Panoramax

Module Overview

All street level modules are located within the modules folder, which contains:

  • the services folder, which contains the "backend ” modules for that service;
  • the svg folder that contains the "frontend ” counterpart;

This is not a strict distinction; some typical frontend functions are performed by the service module.
Both ends have an index.js module to keep track of each implemented layer.
The svg folder also contains layers.js that handles shared functions between layers.

The street level section on the right panel is managed by two modules:

  • renderer/photos.js for the backend.
  • ui/sections/photo_overlays.js for the frontend.

There are also two additional modules:

  • services/pannellum_photo.js that handles the 360° photo viewer;
  • services/plane_photo.js which handles the viewer for plane photos;

These two elements should not be reimplemented within the service module. It can be extended or improved if necessary. The current services that implements these are:

  • Panoramax
  • Vegbilder

You can find the CSS for street level layers in css/60_photos.css.

Test are located into the test folder, there just follow the same path as above to find the corresponding test class. (e.g. test\services\panoramax.js to test modules\services\panoramax.js).

Let's delve into the singular modules to see what they actually do. We will use panoramax as an example.

service/panoramax.js

This module manages the backend (and some frontends) of the Panoramax service.

Here are some functions that are implemented in almost all services:

  • loadTiles and loadTile: these functions retrieve data for a given tile, given a zoom.
  • loadTileDataToCache: called by loadTile, this is where the retrieved data is cached.
d = {
    id: "9b3bfdd7-cd7f-4000-9134-4bcf4b9f0d0e" // image unique ID.
    account_id: "36bf79cb-9607-44bd-8d11-e770f0b495f0" // ID of the account that took the photo.
    capture_time: "2024-07-14 13:39:25.62558+00" // Capture time for the photo, written in human language.
    sequence_id: "e8e45105-76f7-4c01-a1f0-42b85c0eb6d3" // sequence unique ID.
    loc: [9.190192222595215, 45.464034233308496] // location of the respective street level photo.
    capture_time_parsed: Sun Jul 14 2024 15:39:25 GMT+0200 // Capture time for the photo, parsed for better usability.
    heading: 68 // The heading (in degrees) of the photo.
    image_path: "" // URL path of the actual image, this needs to be retrieved later.
    isPano: false // whether this photo is a 360° panorama or a regular "plane" photo. This should be used to select the type of frame to use.
    model: "Google Pixel 6a" // Device used to take the photo.
};

This d variable now contains all the information about a certain image (not the image itself) and can be cached for later use.

  • searchLimited is used to get data from the cache to be rendered later; a limit can be passed to set how many images can be rendered on top of each other.

These are all private functions. The next ones are exported and can be called from the svg functions; some of the more interesting ones are:

  • loadImages and loadLines: these functions act as wrapper functions to call loadTiles, specifying which functions to load (images or sequences).

  • images and sequences: these functions call searchLimited to get the images or sequences visible.

  • setStyles: this functions changes the style of the visible images and sequences, highlighting those selected or moused over.

  • selectImage: perhaps the most bulky functions. When an image bubble is selected, this functions is called:

    • it retrieves the actual image from the API (you should have a dedicated function),
    • the d variable now should also contain something like
      image_path: "https://panoramax.openstreetmap.fr/derivates/9b/3b/fd/d7/cd7f-4000-9134-4bcf4b9f0d0e/sd.jpg" // URL path of the actual image
      This is a mandatory field. This enables the frame to render the photo.
    • it updates the frame (360° or flat) and prepares it for the viewer, adding attribution, arrows to move between the same sequences, and other things like date label or definition checkbox.
    • Calling selectImage(data, keepOrientation) renders the image (data) within the selected frame. The keepOrientation field allows you to keep the same yaw, hfov, and pitch when changing images.
  • ensureViewerLoaded: as the name implies, creates the frame and makes sure it is loaded correctly. It should also contain the step function, which handles moving between images in the same sequence (if supported by the service).

svg/panoramax_image.js

This module manages the frontend of the Panoramax service.

  • filterImages and filterSequences: these functions filter the visible bubbles and lines according to the given filters (right panel).
  • click, mouseover and mouseout: these functions handle pointer events, such as the selection of an image bubble. By clicking on an image, the service should make the frame appear (or update it) and update the style of the selected bubble.
  • showLayer and hideLayer: as the name implies, hide or show the current layer.
  • update: this function is called every time the map is changed (if it is moved, zoomed or whatever), if the layer is enabled, it rearranges the image bubbles and sequence lines to the right place.
  • viewerChanged: is called when an action is performed on the frame; it is mostly used to update the field of view when moving a 360° photo.
  • drawImages: this function handles what to draw based on factors such as zoom and service availability.

Each svg module should also have these 3 functions that should return a boolean:

  • drawImages.enabled, returns whether the layer is enabled
  • drawImages.supported, returns whether the service is available
  • drawImages.rendered, returns whether the layer is currently rendered

These can be implemented as needed

renderer/photos.js

Here you can choose which filter to activate for the streetlevel service. Currently, three filters are available:

  • By date, you can choose from
    • Exact date
    • Year slider
  • By type of photo
  • By user name

To enable these filters in the right panel, you need to add showsLayer('[serviceName]') to one or more of the following functions:

  • photos.shouldFilterByDate, to filter by exact date,
  • photos.shouldFilterDateBySlider, to filter by date using a slider,
  • photos.shouldFilterByPhotoType, to choose between normal or,360-degree photos,
  • photos.shouldFilterByUsername, to filter by username.

The default date filter to use is the Date slider, you should use only one of the two

Let's take an example:

photos.shouldFilterByPhotoType = function() {
    return showsLayer('mapillary') || showsLayer('vegbilder') || showsLayer('panoramax');
};

This means that when mapillary or vegbilder or panoramax is selected, the photo type filter is displayed.

If you want to implement your own filter, you can implement it in the modules\ui\sections\photo_overlays.js class.

service/plane_photo.js and service/pannellum_photo.js

These two modules contain the implementation of a photo frame that can render classic flat photos and 360° panoramic photos.

To add them to your class, you can write something like:

Promise.all([
            pannellumPhotoFrame.init(context, wrapEnter),
            planePhotoFrame.init(context, wrapEnter)
          ]).then(([pannellumPhotoFrame, planePhotoFrame]) => {
            _pannellumFrame = pannellumPhotoFrame;
            _planeFrame = planePhotoFrame;
          });

and then select it by checking whether the image is 360° or not:

_currentFrame = d.isPano ? _pannellumFrame : _planeFrame;

You can later add the selected photo to the frame by calling the _currentFrame.selectPhoto method as described above.

You can also hide/show the viewer by calling the showPhotoFrame and hidePhotoFrame methods.

The plane_photo module is homemade, while you can find here the documentation and API for the pannellum viewer.