You are here

leaflet_more_maps.module in Leaflet More Maps 8

Same filename and directory in other branches
  1. 7 leaflet_more_maps.module
  2. 2.1.x leaflet_more_maps.module

File

leaflet_more_maps.module
View source
<?php

/*
 * @file
 * Leaflet More Maps adds over 20 map options to the Leaflet module.
 *
 * All maps support zoom levels 0..18, except where stated otherwise.
 */
const LEAFLET_MORE_MAPS_NO_CUSTOM_MAPS = 3;

/**
 * Implements hook_leaflet_map_info().
 *
 * All maps show at lat=31, long=-89, zoom=4
 *
 * @return array of map info (includes provider URL, attribution, map features)
 */
function leaflet_more_maps_leaflet_map_info() {
  $default_settings = [
    'attributionControl' => TRUE,
    'closePopupOnClick' => TRUE,
    'doubleClickZoom' => TRUE,
    'dragging' => TRUE,
    'fadeAnimation' => TRUE,
    'layerControl' => FALSE,
    'maxZoom' => 18,
    'minZoom' => 0,
    'scrollWheelZoom' => TRUE,
    'touchZoom' => TRUE,
    'trackResize' => TRUE,
    // Don't specify zoom, if you want to use Auto-box.
    // 'zoom'            =>  2,
    'zoomAnimation' => TRUE,
    'zoomControl' => TRUE,
  ];
  $map_info = [];
  _leaflet_more_maps_assemble_default_map_info($map_info, $default_settings);
  $custom_maps = \Drupal::config('leaflet_more_maps.settings')
    ->get('leaflet_more_maps_custom_maps') ?? [];
  foreach ($custom_maps as $custom_map) {
    _leaflet_more_maps_assemble_custom_map_info($custom_map['map-key'], $custom_map['layer-keys'], $map_info, $default_settings, $custom_map['reverse-order']);
  }
  return $map_info;
}

/**
 * Preprocess function for leaflet_more_maps.
 */
function leaflet_more_maps_preprocess_leaflet_map(array &$variables) {
  $variables['#attached']['library'][] = 'leaflet_more_maps/leaflet-more-maps';
}

/**
 * Implements hook_leaflet_map_info_alter().
 */
function leaflet_more_maps_leaflet_map_info_alter(array &$map_info) {
  @ksort($map_info, SORT_NATURAL | SORT_FLAG_CASE);
}

/**
 * Function to add info for all available maps.
 *
 * @param array $map_info
 *   Map info to add to.
 * @param array $default_settings
 *   Default settings to add to the map.
 */
function _leaflet_more_maps_assemble_default_map_info(array &$map_info, array $default_settings = []) {

  // Use headless protocol rather than logic based on global $is_https;
  $prot = '//';

  // As used by most.
  $zxy = '{z}/{x}/{y}.png';

  // As used by Esri.
  $zyx = '{z}/{y}/{x}.png';

  // Google and Yandex also use x, y and z, but require different URL query
  // templates, see below.
  // Bing uses the quadtree system whereby x, y and z are encoded in a sequence
  // of digits in the range 0..3. See leaflet_more_maps.js for the conversion
  // algorithm.
  $config_settings = Drupal::service('config.factory')
    ->getEditable('leaflet_more_maps.settings');
  $attr_bing = 'Tiles <a href="https://www.bing.com/maps">Bing</a> &copy; Microsoft and suppliers';
  $map_info['bing'] = [
    'label' => 'Bing road + satellite + hybrid (zoom 1..18)',
    'description' => t('Bing road, satellite and hybrid layers'),
    'settings' => [
      'minZoom' => 1,
      'layerControl' => TRUE,
    ] + $default_settings,
    'layers' => [
      'hybrid layer' => [
        'type' => 'quad',
        'urlTemplate' => $prot . 't{s}.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/{q}?it=A,G,L&shading=hill',
        'options' => [
          'attribution' => $attr_bing,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
      'satellite layer' => [
        'type' => 'quad',
        'urlTemplate' => $prot . 'ecn.t{s}.tiles.virtualearth.net/tiles/a{q}?g=1236',
        'options' => [
          'attribution' => $attr_bing,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
      'road layer' => [
        'type' => 'quad',
        'urlTemplate' => $prot . 'ecn.t{s}.tiles.virtualearth.net/tiles/r{q}?g=1236',
        'options' => [
          'attribution' => $attr_bing,
          'subdomains' => [
            0,
            1,
            2,
            3,
            4,
            5,
            6,
            7,
          ],
        ],
      ],
    ],
  ];

  // Esri https://esri.com
  $attr_esri = 'Tiles &copy; <a href="https://esri.com">Esri</a>';
  $esri_names = [
    // Zoom 0..17.
    'World Imagery',
    // Zoom 0..12.
    'NatGeo World Map',
    // Zoom 0..8.
    'World Physical Map',
    // Zoom 0..10.
    'Ocean BaseMap',
    'World Topo Map',
    'World Street Map',
  ];
  $settings = $default_settings;
  foreach ($esri_names as $esri_name) {
    $code = str_replace(' ', '_', $esri_name);
    $url_template = $prot . "server.arcgisonline.com/ArcGIS/rest/services/{$code}/MapServer/tile/{$zyx}";
    switch ($esri_name) {
      case 'NatGeo World Map':
        $label = 'Esri National Geographic (zoom 0..12)';
        $settings['maxZoom'] = 12;
        break;
      case 'World Imagery':
        $label = 'Esri World Imagery (zoom 0..17)';
        $settings['maxZoom'] = 17;
        break;
      case 'World Physical Map':
        $label = 'Esri Physical (zoom 0..8)';
        $settings['maxZoom'] = 8;
        break;
      case 'Ocean BaseMap':
        $label = 'Esri Ocean (zoom 0..10)';
        $settings['maxZoom'] = 10;
        break;
      default:
        $label = "Esri {$esri_name} (zoom 0..18)";
        $settings['maxZoom'] = 18;
    }
    $map_info['esri-' . mb_strtolower($code)] = [
      'label' => $label,
      'description' => $label,
      'settings' => $settings,
      'layers' => [
        'layer' => [
          'urlTemplate' => $url_template,
          'options' => [
            'attribution' => $attr_esri,
          ],
        ],
      ],
    ];
  }
  $attr_google = 'Map data &copy; <a href="https://googlemaps.com">Google</a>';
  $map_info['google-hybrid'] = [
    'label' => 'Google hybrid (zoom 0..18)',
    'description' => t('Google hybrid'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'type' => 'google',
        'urlTemplate' => $prot . 'mt{s}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_google,
          // 'detectRetina' => TRUE,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
      'overlay' => [
        'type' => 'google',
        // Note 'lyrs=h' for transparent overlay.
        'urlTemplate' => $prot . 'mt{s}.google.com/vt/lyrs=h&x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_google,
          'detectRetina' => TRUE,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
    ],
  ];
  $map_info['google-satellite'] = [
    'label' => 'Google satellite (zoom 0..18)',
    'description' => t('Google satellite'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'type' => 'google',
        'urlTemplate' => $prot . 'mt{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_google,
          // 'detectRetina' => TRUE,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
    ],
  ];
  $map_info['google-roadmap'] = [
    'label' => 'Google roadmap (zoom 0..17)',
    'description' => t('Google roadmap'),
    'settings' => [
      'maxZoom' => 17,
    ] + $default_settings,
    'layers' => [
      'layer' => [
        'type' => 'google',
        'urlTemplate' => $prot . 'mt{s}.google.com/vt/x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_google,
          'detectRetina' => TRUE,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
    ],
  ];
  $map_info['google-high-res'] = [
    'label' => 'Google high-res road & terrain (zoom 0..17)',
    'description' => t('Google road & terrain layers, with high-res (Retina) support'),
    'settings' => [
      'layerControl' => TRUE,
      'maxZoom' => 17,
    ] + $default_settings,
    'layers' => [
      'terrain' => [
        'type' => 'google',
        'urlTemplate' => $prot . 'mt{s}.google.com/vt/lyrs=t,r&x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_google,
          'detectRetina' => TRUE,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
      'roadmap' => [
        'type' => 'google',
        // For retina displays we append '&style=high_dpi&w=512',
        // see leaflet_more_maps_preprocess_leaflet_map()
        'urlTemplate' => $prot . 'mt{s}.google.com/vt/x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_google,
          'detectRetina' => TRUE,
          'subdomains' => [
            0,
            1,
            2,
            3,
          ],
        ],
      ],
    ],
  ];
  $mapbox_access_token = $config_settings
    ->get('mapbox_access_token');
  if (empty($mapbox_access_token)) {

    // MapBox as used on leafletjs.com
    $mapbox_access_token = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
  }
  $attr_mapbox = 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>';
  $url_template_mapbox = $prot . "api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}";
  $mapbox_names = [
    'Dark',
    'Light',
    //'Outdoors',
    'Satellite-Streets',
    'Streets',
  ];
  foreach ($mapbox_names as $mapbox_name) {
    switch ($mapbox_name) {
      case 'Dark':
        $id = 'mapbox/dark-v10';
        break;
      case 'Light':
        $id = 'mapbox/light-v10';
        break;
      case 'Outdoors':
        $id = 'mapbox/outdoors-v11';
        break;
      case 'Satellite-Streets':
        $id = 'mapbox/satellite-streets-v11';
        break;
      default:

        // Streets
        $id = 'mapbox/streets-v11';
    }
    $code = mb_strtolower($mapbox_name);
    $label = t("mapbox @name (zoom 0..17)", [
      '@name' => $mapbox_name,
    ]);
    $map_info["mapbox-{$code}"] = [
      'label' => $label,
      'description' => $label,
      'settings' => $default_settings,
      'layers' => [
        'layer' => [
          'urlTemplate' => $url_template_mapbox,
          'options' => [
            'id' => $id,
            'tileSize' => 512,
            'zoomOffset' => -1,
            'accessToken' => $mapbox_access_token,
            'attribution' => $attr_mapbox,
          ],
        ],
      ],
    ];
  }

  // Note: OpenStreetMap OSM Mapnik classic map is available via leaflet.module
  $attr_osm = 'Map data &copy; <a href="https://openstreetmap.org">OpenStreetMap</a> and contributors';

  // OSM Thunderforest
  $attr_thunderforest = '<a href="https://www.thunderforest.com">Thunderforest</a>. ' . $attr_osm;
  $thunderforest_api_key = $config_settings
    ->get('thunderforest_api_key');
  $thunderforest_api_key = empty($thunderforest_api_key) ? '' : '?apikey=' . $thunderforest_api_key;
  $map_info['osm-cycle'] = [
    'label' => 'Thunderforest Cycle (zoom 0..18)',
    'description' => t('OpenStreetMap for cyclists'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/cycle/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-transport'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Transport (zoom 0..18)',
    'description' => t('OpenCycleMap with train & tram lines'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/transport/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-landscape'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Landscape (zoom 0..18)',
    'description' => t('OpenCycleMap with landscape'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/landscape/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-outdoors'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Outdoors (zoom 0..18)',
    'description' => t('OpenStreetMap for outdoor activities'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/outdoors/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-transport-dark'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Transport Dark (zoom 0..18)',
    'description' => t('A dark variant of OSM Thunderforest Transport'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/transport-dark/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-spinal-map'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Spinal Map (zoom 0..18)',
    'description' => t('A perfect map style for all your heavy-rock needs, based on OSM'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/spinal-map/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-pioneer'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Pioneer (zoom 0..18)',
    'description' => t('A turn-of-the-century railroad map style, evoking a sense of bygone mapping, based on OSM'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/pioneer/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-mobile-atlas'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Mobile Atlas (zoom 0..18)',
    'description' => t('A new map style by Thunderforest, based on OSM'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/atlas/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];
  $map_info['osm-neighbourhood'] = [
    // See https://thunderforest.com.
    'label' => 'Thunderforest Neighbourhood (zoom 0..18)',
    'description' => t('Another new map style by Thunderforest, based on OSM'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'tile.thunderforest.com/neighbourhood/' . $zxy . $thunderforest_api_key,
        'options' => [
          'attribution' => $attr_thunderforest,
        ],
      ],
    ],
  ];

  // Wikimedia
  $attr_wiki = 'Wikimedia maps | ' . $attr_osm;
  $map_info['wikimedia'] = [
    'label' => 'Wikimedia (zoom 0..18)',
    'description' => t('Wikimedia Maps'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . "maps.wikimedia.org/osm-intl/{$zxy}",
        'options' => [
          'attribution' => $attr_wiki,
        ],
        'detectRetina' => TRUE,
      ],
    ],
  ];

  // Stamen https://stamen.com
  $attr_stamen = 'Tiles by <a href="https://stamen.com">Stamen Design</a> under <a href="https://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' . $attr_osm;
  $stamen_names = [
    //'Toner',
    'Terrain',
    'Toner-Lite',
    'Toner-Labels',
    'Watercolor',
  ];
  foreach ($stamen_names as $stamen_name) {
    $code = mb_strtolower($stamen_name);
    $max_zoom = $code == 'terrain' || $code == 'watercolor' ? 17 : 18;
    $label = "Stamen {$stamen_name} (zoom 0..{$max_zoom})";
    $url_template = $prot . "stamen-tiles-{s}.a.ssl.fastly.net/{$code}/{$zxy}";
    $map_info["stamen-{$code}"] = [
      'label' => $label,
      'description' => t('@label layer.', [
        '@label' => $label,
      ]),
      'settings' => [
        'maxZoom' => $max_zoom,
      ] + $default_settings,
      'layers' => [
        'layer' => [
          'urlTemplate' => $url_template,
          'options' => [
            'attribution' => $attr_stamen,
          ],
        ],
      ],
    ];
  }

  // HERE maps.
  $attr_here = 'Data &copy; <a href="https://here.com">HERE</a> maps.';
  $here_api_key = $config_settings
    ->get('here_api_key');
  $code = 'base';

  // also 'traffic' and 'aerial'
  $tile_size = 256;

  // or 512
  $format = 'png8';

  // or png or jpg (best for satellite)
  $url_template_here = "https://{s}.{$code}.maps.ls.hereapi.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/{$tile_size}/{$format}?apiKey={$here_api_key}";
  $label = t('HERE Base map (zoom 0..18)');
  $map_info["here-{$code}"] = [
    'label' => $label,
    'description' => $label,
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $url_template_here,
        'options' => [
          'attribution' => $attr_here,
          'subdomains' => [
            1,
            2,
            3,
            4,
          ],
        ],
      ],
    ],
  ];

  /*
  $attr_yandex = 'Map data &copy; <a href="https://maps.yandex.ru">Yandex.Maps</a>';
  $map_info['yandex'] = [
    'label' => 'Yandex Maps (zoom 0..7 in many areas)',
    'description' => t('Yandex roadmap'),
    'settings' => $default_settings,
    'layers' => [
      'layer' => [
        'urlTemplate' => $prot . 'vec0{s}.maps.yandex.net/tiles?l=map&x={x}&y={y}&z={z}',
        'options' => [
          'attribution' => $attr_yandex,
          'subdomains' => [0, 1, 2, 3],
        ],
      ],
    ],
  ];
  */
}

/**
 * Assemble custom map info based on selected layers.
 *
 * @param array $custom_map_key
 * @param array $selected_layer_keys
 * @param array $map_info
 * @param array $default_settings
 * @param false $reverse_order
 */
function _leaflet_more_maps_assemble_custom_map_info(array $custom_map_key, array $selected_layer_keys, array &$map_info, array $default_settings = [], $reverse_order = FALSE) {
  if (empty($custom_map_key) || empty($selected_layer_keys)) {
    return;
  }
  $map_info["~{$custom_map_key}"] = [
    'label' => $custom_map_key,
    'description' => t('Custom defined map with layer switcher'),
    'layers' => [],
  ];
  $i = $reverse_order ? count($selected_layer_keys) : 1;
  foreach ($map_info as $map_key => $map) {
    foreach ($map['layers'] as $layer_key => $layer) {

      // Unique.
      $custom_layer_key = "{$map_key} {$layer_key}";
      if (in_array($custom_layer_key, $selected_layer_keys)) {
        $layer_switcher_key = t('layer') . " #{$i}";
        $map_info["~{$custom_map_key}"]['layers'][$layer_switcher_key] = $layer;
        $i = $reverse_order ? $i - 1 : $i + 1;
      }
    }
  }
  if ($reverse_order) {
    $map_info["~{$custom_map_key}"]['layers'] = array_reverse($map_info["~{$custom_map_key}"]['layers']);
  }
  $has_layer_control = count($map_info["~{$custom_map_key}"]['layers']) > 1;
  $map_info["~{$custom_map_key}"]['settings'] = [
    'layerControl' => $has_layer_control,
  ] + $default_settings;
}

Functions

Namesort descending Description
leaflet_more_maps_leaflet_map_info Implements hook_leaflet_map_info().
leaflet_more_maps_leaflet_map_info_alter Implements hook_leaflet_map_info_alter().
leaflet_more_maps_preprocess_leaflet_map Preprocess function for leaflet_more_maps.
_leaflet_more_maps_assemble_custom_map_info Assemble custom map info based on selected layers.
_leaflet_more_maps_assemble_default_map_info Function to add info for all available maps.

Constants