utils_gazetteer.mjs

/**
## mapp.utils.gazetteer{}

@module /utils/gazetteer
*/

export function datasets(term, gazetteer) {

  if (!gazetteer.provider) {

    // The default gazetteer config is for a dataset search.
    // Abort current dataset query. Onload will not be called.
    gazetteer.xhr?.abort()
    gazetteer.xhr = new XMLHttpRequest()
    gazetteer.qterm && search(gazetteer)
  }

  // Search additional datasets.
  gazetteer.datasets?.forEach(dataset => {

    // Abort current dataset query. Onload will not be called.
    dataset.xhr?.abort()
    dataset.xhr = new XMLHttpRequest()

    search({
      layer: gazetteer.layer,
      table: gazetteer.table,
      query: gazetteer.query,
      qterm: gazetteer.qterm,
      label: gazetteer.label,
      title: gazetteer.title,
      limit: gazetteer.limit,
      no_result: gazetteer.no_result,
      leading_wildcard: gazetteer.leading_wildcard,
      callback: gazetteer.callback,
      maxZoom: gazetteer.maxZoom,
      ...dataset
    })
  })

  function search(dataset) {

    const layer = gazetteer.mapview.layers[dataset.layer]

    // Skip if layer defined in datasets is not added to the mapview
    if (!layer) {

      console.warn('No layer definition for gazetteer search.')
      return;
    }

    // Skip if layer table is not defined and no table is defined in dataset or gazetteer.
    if (!layer.table && !dataset.table) {

      console.warn('No table definition for gazetteer search.')
      return;
    };

    dataset.xhr.open('GET', gazetteer.mapview.host + '/api/query?' +
      mapp.utils.paramString({
        template: dataset.query || 'gaz_query',
        label: dataset.label || dataset.qterm,
        qterm: dataset.qterm,
        qID: layer.qID,
        locale: gazetteer.mapview.locale.key,
        layer: layer.key,
        filter: layer.filter?.current,
        table: dataset.table || layer.table,
        wildcard: '*',
        term: `${dataset.leading_wildcard ? '*' : ''}${term}*`,
        limit: dataset.limit || 10
      }))

    dataset.xhr.setRequestHeader('Content-Type', 'application/json')
    dataset.xhr.responseType = 'json'
    dataset.xhr.onload = e => {

      // The gazetteer input may have been cleared prior to the onload event.
      if (!gazetteer.input.value.length) return;

      if (e.target.status >= 300) return;

      // No results
      if (!e.target.response) {
        if (dataset.no_result === null) return;
        gazetteer.list.append(mapp.utils.html.node`
          <li>
            <span class="label">${dataset.title || layer.name}</span>
            <span>${dataset.no_result || mapp.dictionary.no_results}</span>`)
        return;
      }

      // Ensure that response if a flat array.
      [e.target.response].flat().forEach(row => {

        gazetteer.list.append(mapp.utils.html.node`
          <li onclick=${e => {

            if (dataset.callback) return dataset.callback(row, dataset);

            mapp.location.get({
              layer,
              id: row.id
            }).then(loc => loc?.flyTo?.(dataset.maxZoom))

          }}>
            <span class="label">${dataset.title || layer.name}</span>
            <span>${row.label}</span>`)
      })

    }

    dataset.xhr.send()
  }
}

export function getLocation(location, gazetteer) {

  if (typeof gazetteer.callback === 'function') {
    gazetteer.callback(location)
    return;
  }

  const coord = ol.proj.transform(
    [location.lng, location.lat],
    `EPSG:4326`,
    `EPSG:${gazetteer.mapview.srid}`
  )

  if (!ol.extent.containsCoordinate(gazetteer.mapview.extent, coord)) {
    alert(mapp.dictionary.invalid_lat_lon);
    return;
  }

  Object.assign(location, {
    layer: {
      mapview: gazetteer.mapview
    },
    Layers: [],
    hook: location.label
  })

  const infoj = [
    {
      title: location.label,
      value: location.source,
      inline: true
    },
    {
      type: 'pin',
      value: [location.lng, location.lat],
      srid: '4326',
      class: 'display-none',
      location
    }
  ]

  mapp.location.decorate(Object.assign(location, { infoj }))

  gazetteer.mapview.locations[location.hook] = location

  location.flyTo(gazetteer.maxZoom)
}