Data privacy notice

 

When this content is loaded, usage information is transmitted to Vimeo and may be processed there.

 

             

Form widget: Map integration

Modified on Wed, 24 Apr at 3:00 PM

If you use the map widget to show map data, always make sure to check the terms of usage and licensing conditions of the map data provider! For demonstration purposes, we make use of map data from https://tile.openstreetmap.org, see http://www.openstreetmap.org/copyright. 



By default, the widget uses map data from Open Street Maps. You need to ensure this is compatible with privacy related regulations. Alternatively, you can configure a custom map provider.


The map widget adds a new form element to the form designer. It lets you show an interactive world map to the user. Uses can then select a certain type of geometry in the map: either a list of disconnected points, a line consisting of many line segments, or a polygonal area. The selected geometry is stored in a hidden textarea field and transmitted to the server when the form is submitted. When an existing form record is reopened, the map focuses on the selected geometry automatically.


Whether the user can select points, a line, or an area can be configured in the form designer. You can also to disallow selecting any kind of geometry, which can be useful if you only want to add a map to the form for illustrative purposes. Finally, you can also choose an initial location and zoom level for the map in the form designer.


If necessary, you can also show custom map data from a service provider. Supported protocols are WMTS, WMS und TMS.


Contents



The map widgets lets users select points, lines, or areas on a map.


Properties

You can adjust several properties for the map widget to customize the map layout and to use custom map data.


Basic map configuration. You can select an area of the map to show to users and choose the geometry types that users may select.
  • Latitude and longitude - The initial position on the map that is shown when no geometry was selected yet.
  • Zoom min, zoom max. - The minimum and maximum allowed zoom levels at which the user may view the map.
  • Zoom initial - Initial zoom level applied when no geometry was selected yet.
  • Select points / line / area - Whether users can select points, a line, or an area in the map. Multiple geometry types can be selected, the user can then choose a type to select with the buttons at the top left corner of the map. When no types are selected, the user cannot select any geometry in the map. This might be useful when you only want to include a map in your form.
  • Locate button: When enabled, adds a locate button to the map. If the user click on the button, their current location is checked and shown on the map. The map is scrolled such that the user can see their position. 
  • Locate immediately - When enabled, and no geometry was selected, locates the user immediately when the form is opened. When a form is openend in the inbox, the automatic location feature is always disabled. We recommend that you avoid using this option, as it results in the browser asking the user to share their location as soon as they open the form.
  • Custom map data - Lets you use custom map data, see the section below.



You can also customize the color of the markers in the map.
  • Points - Controls the color of the bead-shaped point marker icons in the map.
  • Location - Controls the color of the marker for the user's position.
  • Line - Controls the color of the line, when the geometry type is set to line.
  • Area: border - Controls the color of the border of an area,  when the geometry type is set to area.
  • Area: fill - Controls the color of the inside of an area, when the geometry type is set to area.


Custom map data

You can also include custom map data provided by map services. The map widget currently supports the protocols WMTS, WMS, and TMS. If you use map data, make sure you check the terms of service and the licensing conditions of the map provider! The map widget offers an input field where you can enter a custom attribution text that is shown at the bottom right corner of the map widget.


WMTS and TMS

WMTS and TMS are quite similar. The only difference is that for TMS, the y coordinate is inverted with respect to WMTS.



Here we use an URL template to load map data from a WMTS map service.


Map services that implement the WMTS or TMS protocol are easy to integrate into the map widget. You only need to enter the appropriate URL of the map service. The placeholders {x} (latitude), {y} (longitude) and {z} (zoom level) in the URL are replaced with the corresponding values when requesting map data from the map service.


WMS

Inegrating a WMS map service is a bit more difficult. See also WMS in Leaflet.


Most of the required data can be obtained from the GetCapabilities method of the WMS map service. If the base URL of the map service, for example, is https://webmap4.lroc.asu.edu/, you can access its capabilities via the URL https://webmap4.lroc.asu.edu/?request=GetCapabilities erfolgen. This URL gives you an XML file with the capabilities of the web service.



Here we integrate a map of the moon via a WMS map service. We load 2 layers, one layer with the imagery, and one layer with the names of well-know locations on the moon.


  • Link - Must be the base URL of the WMS map service.
  • Version - The version of the WMS map service. The default is 1.1.1, which is supported by most services.
  • Coordinates - Spatial reference system (SRS) to use when requesting map data. As long as the map service supports the SRS, the value you choose here is not important. You can find the supported SRS within the <srs> XML element of the service's capabilities descriptor.
  • Layers - Layers that are loaded from the map service, separated by a comma. A common use case as shown in the illustration above is to load one layer with the image data and one layer with the labels. You can find the supported layers within the <Layer> XML elements of the service's capabilities descriptor.
  • Separate layers - If disabled: The map service is responsible for combining the requested layers into the final image. This is the recommended settings, as the map service is usually better at combining the layers into an appropriate image. If disabled: The layers are loaded separately from the web service and placed on top of each other.
  • Styles - Optional styles to apply, if the map service supports it.
  • Format - Image format of the map data. The web service must supported the entered image format. You can find the supported formats within the <Format> XML elements of the service's capabilities descriptor.
  • Capitalize parameters großschreiben - When enabled, all parameters (such as format=image/png or version=1.1.1) are capitalized when making a request to the map service. Must be enabled if the map service requires it.


Localization

You can customize the localized messages in the form by editing the I18N variables in the backend. The plugin only ships with support for German and English, this lets you support other languages as well. The I18N keys and their default values are as follows:

  • mapActionLocate - Show your current position on the map
  • mapActionSelectArea - Select an area on the map
  • mapActionSelectLine - Select a line on the map
  • mapActionSelectPoints - Select points on the map
  • mapActionReset - Reset selected points
  • mapActionUserPosition - You are here
  • mapActionZoomIn - Zoom in
  • mapActionZoomOut - Zoom out


JavaScript-API

For advanced use cases, you can interact with the map widget programmatically via JavaScript. The entry point for the API is provided by the global object $.xutil.xMapApi.


Some examples:

// Get the currently selected geometry, as a set of points
$.xutil.xMapApi.instance("[data-name=map1]").selectedGeometry()

// Select a different type of geometry
$.xutil.xMapApi.instance("[data-name=map1]").selectGeometry({
  type: "area",
  points: [
    {lat: 51.0916,lng: 13.7622},
    {lat: 51.0148, lng: 13.7375},
    {lat: 51.0593, lng: 13.8621}
  ],
})

// Remove the attribution link at the bottom right corner of the map completely for a specific map
$.xutil.xMapApi.whenRendered("[data-org-name=map2]").then(api => api.leafletMap().attributionControl.remove())

// Remove the attribution link at the bottom right corner of the map completely for all maps
$.xutil.xMapApi.addRenderedListener(api => api.leafletMap().attributionControl.remove())


The entire API looks as folows. $.xutil.xMapApi is the main entry point of the map widget API, which is described by the interface IXMapApi. This interface has the following definition:

/**
 * API entry point for the XMap widget. Provides methods to access particular
 * map widget instances.
 *
 * For example:
 *
 * If you want to get or set the value of a map widget:
 *
 * ```js
 * // Get the value
 * $.xutil.xMapApi.instance("[data-name=map1]").selectedGeometry()
 *
 * // Set the value
 * $.xutil.xMapApi.instance("[data-name=map1]").selectGeometry({
 * type: "area",
 * points: [{lat: 51.0916,lng: 13.7622}, {lat: 51.0148, lng: 13.7375}, {lat: 51.0593, lng: 13.8621}],
 * })
 * ```
 *
 * If you want to remove the attribution links at the bottom right corner:
 *
 * ```js
 * // For a specific map
 * $.xutil.xMapApi.whenRendered("[data-org-name=map2]")
 * .then(api => api.leafletMap().attributionControl.remove())
 *
 * // For all maps
 * $.xutil.xMapApi.addRenderedListener(api => api.leafletMap().attributionControl.remove())
 * ```
 */
interface IXMapApi {
  /**
   * Adds a listener that is called when a map widget was rendered. Once
   * the widget was rendered, the Leaflet instance can be accessed. This
   * method can be used for logic that should be applied to every map widget.
   *
   * A map widget is rendered only once it has become visible, to avoid UI bugs.
   *
   * If the map widget is already visible, the listener is called
   * immediately (but asynchronously).
   *
   * Each listener is called at most once, but may never be called when the widget
   * is not on the first page and the user navigates to the page with the widget.
   * @param listener Listener to call when the widget is rendered.
   */
  addRenderedListener: (listener: (widget: IXMapWidget) => void) => void;

  /**
   * Removes a listener that was added via {@link addRenderedListener}. The listener
   * will not be invoked anymore for any further widgets when they are rendered.
   * @param listener Listener to remove.
   */
  removeRenderedListener: (listener: (widget: IXMapWidget) => void) => void;

  /**
   * Gets the API for the given map widget from either the container element of a child
   * of the map.
   * @param mapElement A map widget element with the class `XMapContainer`; or
   * any child thereof. Can be a native HTMLElement, a list of HTMLElements, a
   * CSS selector, or a JQuery wrapper.
   * @throws When the given element is not an HTMLElement or not a map element.
   */
  instance: (mapElement: TElementSpecifier<HTMLElement>) => IXMapWidget;

  /**
   * Finds all map widget instances within the base element. If the base element is
   * a map widget instance or a child there of, that map widget is returned.
   * @param base Base element where to search to for map widgets. Can be a native
   * HTMLElement or list of HTMLElements, a CSS selector, or a JQuery wrapper.
   * @returns A list with all map widgets.
   */
  instances: (base?: TElementSpecifier<HTMLElement>) => IXMapWidget[];

  /**
   * Returns a promise that resolves when the given map widget was rendered
   * and the user can interact with it.
   *
   * A map widget is initialized when it is visible, to prevent UI bugs.
   *
   * Note: If the user never navigates to the page containing the map widget, the
   * returned promise may never be settled!
   *
   * Consider using {@link instance} if you just want to e.g. retrieve data from the map.
   * @param mapElement A map widget element with the class `XMapContainer`; or
   * any child thereof. Can be a native HTMLElement, a list of HTMLElements, a
   * CSS selector, or a JQuery wrapper.
   * @throws When the given element is not an HTMLElement or not a map element.
   */
  whenRendered: (
    mapElement: TElementSpecifier<HTMLElement>
  ) => Promise<IXMapWidget>;
}

/**
 * Wraps a particular map widget instance and provides methods for that map widget.
 *
 * Most methods work regardless of whether the map widget was already rendered. The
 * {@link leafletMap} will return undefined when the map widget was not rendered yet.
 */
interface IXMapWidget {
  /**
   * Gets the container element of this map widget.
   * @returns The container element of the map widget, with the class `XMapDiv`.
   */
  container: () => HTMLElement;

  /**
   * Clears the selected geometry, if any geometry was selected.
   * @param silent When `true`, do not fire any change or blur events
   * on the text area element. Defaults to `false`.
   */
  clear: (silent?: boolean) => void;

  /**
   * Whether the widget was already rendered. The widget is rendered only once it has become
   * visible. When the widget was not yet rendered, {@link getLeaflet} returns `undefined`.
   */
  rendered: () => boolean;

  /**
   * (Re-)renders the widget, if possible. A map widget can be rendered only
   * when it is visible, to prevent UI bugs.
   */
  renderIfPossible: () => void;

  /**
   * Gets the currently selected geometry.
   * @returns geometry The geometry currently shown on the map.
   */
  selectedGeometry: () => IXMapGeometry;

  /**
   * Sets the selected geometry to the given value.
   * @param geometry Geometry to show on the map.
   * @param silent When `true`, do not fire any change or blur events
   * on the text area element. Defaults to `false`.
   */
  selectGeometry: (geometry: IXMapGeometry, silent?: boolean) => void;

  /**
   * Queries the browser for the user's current location and adds a marker to
   * the map with the user's location.
   * @returns The position of the user.
   * @throws When the user's location could not be determined. May happen e.g.
   * when the browser does not support the geolocation feature or when the user
   * denied access to their location.
   */
  showGeoLocation: () => Promise<GeolocationPosition>;

  /**
   * Hides the location of the user, if previously shown by {@link showGeoLocation}.
   * When no geo location is currently visible, does nothing.
   */
  hideGeoLocation: () => void;

  /**
   * Gets the instance of the backing Leaflet widget. Can be used for advanced scripting.
   * Note that the leaflet is created only once the map has become visible. This method
   * returns `undefined` when no leaflet exists yet.
   * @returns The instance of the backing Leaflet widget. `undefined` if the map was
   * not rendered yet.
   */
  leafletMap: () => import("leaflet").Map | undefined;

  /**
   * Gets the instance of the backing Leaflet widget. Can be used for advanced scripting.
   * This returns a promise that resolves once the Leaflet widget was created. It is
   * created once the map element becomes visible. If the user never navigates to the
   * page with the widget, it may never become visible and consequently, the returned
   * promise may never be settled.
   * @returns The instance of the backing Leaflet widget.
   */
  waitForLeafletMap: () => Promise<import("leaflet").Map>;
}

/**
 * Describes the value (geometry) that can be selected by the map widget. Consists
 * of the type of the selected geometry (point, line, or area), and a list of points
 * for the geometry.
 */
interface IXMapGeometry {
  /**
   * The type of the geometry, either a point, line, or area.
   */
  readonly type: TGeometryType;

  /**
   * The points of the selected geometry. When the type is `line`,
   * the points are connected in order. When the type is `area`, the
   * points are the corner points of a polygon.
   */
  readonly points: LatLngLiteral[];
}

/**
 * The type of the geometry that can be selected.
 * - point Selects a set of disconnected points
 * - line Select a single line going through all points
 * - area Selects a polygon with the points as its corner points
 */
type TGeometryType = "point" | "line" | "area";

/**
 * A coordinate for a position on the surface of a spheroid object,
 * consisting of a latitude and longitude.
 */
interface LatLngLiteral {
  /** The latitude of the coordinate */
  lat: number;
  /** The longitude of the coordinate */
  lng: number;
}

/**
 * Union of types that can be interpreted as referencing to one or multiple
 * (DOM) elements.
 * - `undefined` and `null` are represent a missing element.
 * - `TElement` is the element itself.
 * - `TElement[]` is an array of elements.
 * - `NodeListOf<TElement>` is a list of elements, returned by
 * `querySelectorAll`.
 * - `HTMLCollectionOf<TElement>` is a collection of elements, returned by
 * `getElementsByClassName`.
 * - `string` is a CSS selector for the element(s).
 * - `JQuery` is a JQuery wrapper with the element(s).
 * @typeParam TElement - Type of the DOM element.
 * @returns A union type with various different types corresponding to an
 * element.
 */
type TElementSpecifier<TElement extends Element> =
  | undefined
  | null
  | TElement
  | TElement[]
  | (NodeList & NodeListOf<TElement>)
  | (HTMLCollection & HTMLCollectionOf<TElement>)
  | string
  | JQuery<TElement>;


Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article