View source
<?php
namespace Drupal\ip_geoloc\Plugin\views\style;
use Drupal\core\form\FormStateInterface;
use Drupal\views\Plugin\views\style\StylePluginBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\ip_geoloc\Services\IpGeoLocAPI;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\ip_geoloc\Services\IpGeoLocGlobal;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\ip_geoloc\Services\IpGeoLocSession;
use Drupal\Component\Utility\Xss;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Unicode;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\ip_geoloc\Services\IpGeoLocViewsPluginStyle;
use Drupal\Core\Config\ConfigFactory;
define('LEAFLET_MARKERCLUSTER_EXCLUDE_FROM_CLUSTER', 1);
define('LEAFLET_SYNC_CONTENT_TO_MARKER', 1 << 1);
define('LEAFLET_SYNC_MARKER_TO_CONTENT', 1 << 2);
define('LEAFLET_SYNC_MARKER_TO_CONTENT_WITH_POPUP', 1 << 3);
define('LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT', 1 << 4);
class IpGeoLocPluginStyleLeaflet extends StylePluginBase implements ContainerFactoryPluginInterface {
protected $usesGrouping = TRUE;
protected $usesFields = TRUE;
public $moduleHandler;
public $ipGeolocGlobal;
public $api;
public $stringTranslation;
public $ipGeolocSession;
public $viewPluginStyle;
public $config;
public function __construct(array $configuration, $plugin_id, $plugin_definition, IpGeoLocAPI $api, ModuleHandler $moduleHandler, IpGeoLocGlobal $ipGeolocGlobal, TranslationInterface $stringTranslation, IpGeoLocSession $ipGeolocSession, IpGeoLocViewsPluginStyle $viewPluginStyle, ConfigFactory $config_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->api = $api;
$this->moduleHandler = $moduleHandler;
$this->ipGeolocGlobal = $ipGeolocGlobal;
$this->stringTranslation = $stringTranslation;
$this->session = $ipGeolocSession;
$this->viewPluginStyle = $viewPluginStyle;
$this->config = $config_factory
->get('ip_geoloc.settings');
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('ip_geoloc.api'), $container
->get('module_handler'), $container
->get('ip_geoloc.global'), $container
->get('string_translation'), $container
->get('ip_geoloc.session'), $container
->get('ip_geoloc.views_plugin_style'), $container
->get('config.factory'));
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['map'] = [
'default' => 'OSM Mapnik',
];
$options['map_height'] = [
'default' => 300,
];
$latitude = $this->moduleHandler
->moduleExists('location') ? 'location_latitude' : 'ip_geoloc_latitude';
$longitude = $this->moduleHandler
->moduleExists('location') ? 'location_longitude' : ($latitude == 'ip_geoloc_latitude' ? 'ip_geoloc_longitude' : $latitude);
$options['ip_geoloc_views_plugin_latitude'] = [
'default' => $latitude,
];
$options['ip_geoloc_views_plugin_longitude'] = [
'default' => $longitude,
];
$options['default_marker'] = [
'contains' => [
'default_marker_color' => [
'default' => '',
],
'default_marker_special_char' => [
'default' => '',
],
'default_marker_special_char_class' => [
'default' => '',
],
],
];
$options['visitor_marker_leaflet'] = [
'contains' => [
'visitor_marker_color' => [
'default' => '',
],
'visitor_marker_special_char' => [
'default' => '',
],
'visitor_marker_special_char_class' => [
'default' => '',
],
'visitor_marker_accuracy_circle' => [
'default' => FALSE,
],
],
];
$options['differentiator'] = [
'contains' => [
'differentiator_field' => [
'default' => '',
],
],
];
$options['center_option'] = [
'default' => 0,
];
$options['tags'] = [
'contains' => [
'marker_tag' => [
'default' => '',
],
'tag_css_class' => [
'default' => 'tag-inside-marker',
],
],
];
$options['tooltips'] = [
'contains' => [
'marker_tooltip' => [
'default' => '',
],
],
];
$options['sync'] = [
'contains' => [
LEAFLET_SYNC_CONTENT_TO_MARKER => [
'default' => FALSE,
],
LEAFLET_SYNC_MARKER_TO_CONTENT => [
'default' => FALSE,
],
LEAFLET_SYNC_MARKER_TO_CONTENT_WITH_POPUP => [
'default' => TRUE,
],
LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT => [
'default' => TRUE,
],
],
];
$options['full_screen'] = [
'default' => FALSE,
];
$options['scale_metric'] = [
'default' => FALSE,
];
$options['scale_imperial'] = [
'default' => FALSE,
];
$options['zoom_indicator'] = [
'default' => FALSE,
];
$options['open_balloons_on_click'] = [
'default' => TRUE,
];
$options['open_balloons_on_hover'] = [
'default' => FALSE,
];
$options['goto_content_on_click'] = [
'default' => FALSE,
];
$options['map_reset'] = [
'default' => FALSE,
];
$options['map_reset_css_class'] = [
'default' => 'R',
];
$options['map_cluster_toggle'] = [
'default' => FALSE,
];
$options['mini_map'] = [
'contains' => [
'on' => [
'default' => FALSE,
],
'height' => [
'default' => 100,
],
'width' => [
'default' => 150,
],
'toggle' => [
'default' => TRUE,
],
'scope_color' => [
'default' => 'red',
],
'zoom_delta' => [
'default' => -5,
],
],
];
$options['cluster_radius'] = [
'default' => $this->moduleHandler
->moduleExists('leaflet_markercluster') ? 80 : '',
];
$options['disable_clustering_at_zoom'] = [
'default' => '',
];
$options['cluster_differentiator'] = [
'contains' => [
'cluster_differentiator_fields' => [
'default' => '',
],
'zoom_ranges' => [
'default' => [],
],
'cluster_tooltips' => [
'default' => TRUE,
],
'cluster_outline' => [
'default' => 0,
],
'cluster_touch_mode' => [
'default' => 1,
],
],
];
$options['cluster_aggregation'] = [
'contains' => [
'aggregation_field' => [
'default' => '',
],
'aggregation_function' => [
'default' => '',
],
'ranges' => [
'contains' => [],
],
'precision' => [
'default' => '',
],
],
];
$range = 10;
foreach ([
'small',
'medium',
'large',
] as $size) {
$options['cluster_aggregation']['contains']['ranges']['contains'][$size] = [
'default' => $range,
];
$range *= 10;
}
$options['disable_clustering_at_zoom'] = [
'default' => '',
];
$options['empty_map_center'] = [
'default' => '',
];
$options['map_options_leaflet'] = [
'contains' => [
'maxzoom' => [
'default' => 18,
],
'zoom' => [
'default' => 2,
],
'zoom_on_click' => [
'default' => '',
],
'center_lat' => [
'default' => '',
],
'center_lon' => [
'default' => '',
],
'scrollwheelzoom' => [
'default' => TRUE,
],
'dragging' => [
'default' => TRUE,
],
'separator' => [
'default' => '<br/>',
],
],
];
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$path = drupal_get_path('module', 'ip_geoloc');
if (strpos($this->ipGeolocGlobal
->markerDirectory(), 'amarkers')) {
$form['#attached']['library'][] = 'ip_geoloc/admin_css_amarker';
}
else {
$form['#attached']['library'][] = 'ip_geoloc/admin_css_marker';
}
$form_state
->set('renderer', 'leaflet');
$weight = 1;
$this
->addMapAndHeight($form, $weight);
$this->viewPluginStyle
->pluginStyleBulkOfForm($this, $form, $form_state);
$form['center_option']['#options'][0] = $this
->t('Auto-box to fit all markers (include visitor marker if color <strong>not</strong> set to <none>)');
$lib_markercluster = $this->moduleHandler
->moduleExists('leaflet_markercluster') ? 'leaflet_markercluster/leaflet-markercluster' : FALSE;
$fields = ip_geoloc_get_display_fields($this->displayHandler, FALSE, TRUE);
$this
->addDefaultMarker($form, $weight);
$this
->addVisitorMarker($form, $weight);
$this
->addCheckBoxes($form, $lib_markercluster, $weight);
$this
->addMiniMapInset($form, $weight);
$this
->addMarkerTags($form, $fields, $weight);
$this
->addMarkerTooltips($form, $fields, $weight);
$this
->addSync($form, $weight);
$this
->addMarkerCluster($form, $lib_markercluster, $weight);
$this
->addClusterDifferentiator($form, $form_state, $lib_markercluster, $weight);
$this
->addMoreMapOptions($form, $weight);
}
public function setRenderedFields($rendered_fields = []) {
$this->rendered_fields = $rendered_fields;
}
public function getRenderedFields() {
return $this->rendered_fields;
}
private function addMapAndHeight(&$form, &$weight) {
$maps = [];
foreach ($this
->pluginStyleLeafletMapGetInfo() as $key => $map) {
$maps[$key] = $map['label'];
}
$form['map'] = [
'#title' => $this
->t('Map'),
'#type' => 'select',
'#options' => $maps,
'#default_value' => $this->options['map'],
'#required' => TRUE,
'#weight' => $weight++,
];
$desc1 = $this
->t('Examples: <em>250</em> or <em>50em</em> or <em>40vh</em> (percentage of viewport height).');
$desc2 = $this
->t('If left blank, the height defaults to 300 pixels. The width of the map will extend to its bounding container.');
$desc3 = $this
->t('You may enter <em><none></em>. If you do, then the height attribute must be set through Javascript or CSS elsewhere or the map will not display. CSS example: <em>.ip-geoloc-map .leaflet-container { height: 150px; }</em>');
$form['map_height'] = [
'#title' => $this
->t('Map height'),
'#type' => 'textfield',
'#size' => 7,
'#default_value' => $this->options['map_height'],
'#description' => $desc1 . '<br/>' . $desc2 . '<br/>' . $desc3,
'#weight' => $weight++,
];
}
private function addDefaultMarker(&$form, &$weight) {
$path = drupal_get_path('module', 'ip_geoloc');
$desc1 = $this
->t('In addition to selecting a color, you may superimpose a special icon on top of each marker. <br><a target="fsymbols" href="!url_fsymbols">fsymbols</a> characters can be copied and pasted straight into the <strong>Font icon character</strong> field. Other libraries like <a target="fontawesome" href="!url_fontawesome">Font Awesome</a> and <a target="flaticon" href="!url_flaticon">flaticon</a> use names that you type in the <strong>Font icon class</strong> field.', [
'!url_fsymbols' => 'http://fsymbols.com',
'!url_fontawesome' => 'http://fortawesome.github.io/Font-Awesome/cheatsheet',
'!url_flaticon' => 'http://flaticon.com',
]);
$desc2 = $this
->t('<em>fsymbols</em> require no further installation. For other libraries see the <a target="readme" href="!url_readme">README</a>.', [
'!url_readme' => "{$path}/README.txt",
]);
$desc3 = $this
->t('All this works best with the markers from the <em>/amarkers</em> directory, configurable <a target="config" href="!url_config">here</a>.', [
'!url_config' => 'admin/config/system/ip_geoloc',
]);
$form['default_marker'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Default marker style'),
'#description' => $desc1 . '<br/>' . $desc2 . '<br/>' . $desc3,
'#weight' => $weight++,
];
$form['default_marker']['default_marker_color'] = [
'#title' => $this
->t('Style/color'),
'#type' => 'select',
'#default_value' => $this->options['default_marker']['default_marker_color'],
'#options' => $this->ipGeolocGlobal
->markerColors(),
'#description' => $this
->t('Select an image to use for all location markers whose images are not overridden by the <strong>Location differentiator</strong> below.'),
'#attributes' => [
'class' => [
'marker-color',
],
],
];
$form['default_marker']['default_marker_special_char'] = [
'#title' => $this
->t('Font icon character'),
'#type' => 'textfield',
'#size' => 8,
'#default_value' => $this->options['default_marker']['default_marker_special_char'],
'#description' => $this
->t('Paste directly from <a target="fsymbols" href="!url_fsymbols">fsymbols</a>. If the character displays in color or as a square then it may not save or display correctly.', [
'!url_fsymbols' => 'http://text-symbols.com',
]),
];
$desc4 = $this
->t('Use the class name from the font icon library you are using. Append <strong>light</strong>, <strong>dark</strong> or <strong>red</strong> to change the color. Examples:<br/>Font Awesome: <strong>fa fa-beer light</strong><br/>flaticon: <strong>flaticon-bicycle12 red</strong>');
$form['default_marker']['default_marker_special_char_class'] = [
'#title' => $this
->t('Font icon class'),
'#type' => 'textfield',
'#size' => 25,
'#default_value' => $this->options['default_marker']['default_marker_special_char_class'],
'#description' => $desc4,
];
}
private function addVisitorMarker(&$form, &$weight) {
$form['visitor_marker_leaflet'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Visitor marker style'),
'#description' => $this
->t('For the visitor marker to show, enable the <em>Set my location</em> block and/or tick the option to periodically reverse-geocode via Google, under the <a href="@config_page">Data collection options</a>.', [
'@config_page' => 'admin/config/system/ip_geoloc',
]),
'#weight' => $weight++,
];
$visitor_marker_colors = [
'none' => '<' . $this
->t('none') . '>',
] + $this->ipGeolocGlobal
->markerColors();
unset($visitor_marker_colors['0']);
$form['visitor_marker_leaflet']['visitor_marker_color'] = [
'#title' => $this
->t('Style/color'),
'#type' => 'select',
'#multiple' => FALSE,
'#default_value' => $this->options['visitor_marker_leaflet']['visitor_marker_color'],
'#options' => $visitor_marker_colors,
'#attributes' => [
'class' => [
'marker-color',
],
],
];
$form['visitor_marker_leaflet']['visitor_marker_special_char'] = [
'#title' => $this
->t('Font icon character'),
'#type' => 'textfield',
'#size' => 8,
'#default_value' => $this->options['visitor_marker_leaflet']['visitor_marker_special_char'],
'#description' => $this
->t('As above'),
];
$form['visitor_marker_leaflet']['visitor_marker_special_char_class'] = [
'#title' => $this
->t('Font icon class'),
'#type' => 'textfield',
'#size' => 25,
'#default_value' => $this->options['visitor_marker_leaflet']['visitor_marker_special_char_class'],
'#description' => $this
->t('As above.'),
];
$form['visitor_marker_leaflet']['visitor_marker_accuracy_circle'] = [
'#title' => $this
->t('Accuracy circle'),
'#type' => 'checkbox',
'#default_value' => $this->options['visitor_marker_leaflet']['visitor_marker_accuracy_circle'],
'#description' => $this
->t("Display a circle depicting where the visitor's real location is most likely to be."),
];
}
private function addMarkerTags(&$form, $fields, &$weight) {
$form['tags'] = [
'#title' => $this
->t('Marker tags'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => empty($this->options['tags']['marker_tag']),
'#description' => $this
->t('Each marker may have a tag. A tag is a number or short text shown permanently above, below or inside the marker.'),
'#weight' => $weight++,
];
$form['tags']['marker_tag'] = [
'#title' => $this
->t('Views field to populate tags'),
'#type' => 'select',
'#default_value' => $this->options['tags']['marker_tag'],
'#options' => $fields,
'#description' => $this
->t('Example: "Content: Title". Use "Global: View result counter" if you want to number your locations.'),
];
$form['tags']['tag_css_class'] = [
'#title' => $this
->t('Tag position and style'),
'#type' => 'textfield',
'#default_value' => $this->options['tags']['tag_css_class'],
'#description' => $this
->t('The CSS class or classes applied to each tag. Tagged marker CSS classes coming with this module are <strong>tag-above-marker</strong>, <strong>tag-below-marker</strong> and <strong>tag-inside-marker</strong>. If you opted to have <em>no markers</em>, i.e. tags only, you may use <strong>tag-rounded-corners</strong> or <strong>tag-pointy-circle</strong>, which is recommended for numbers. You may also create your own CSS classes and use them here.'),
];
}
private function addMarkerTooltips(&$form, $fields, &$weight) {
$form['tooltips'] = [
'#title' => $this
->t('Marker tooltips'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => empty($this->options['tooltips']['marker_tooltip']),
'#description' => $this
->t('In addition to balloons, which pop up when markers are <em>clicked</em>, you can have tooltips. A tooltip is a short text that appears when you <em>hover</em> over a marker.'),
'#weight' => $weight++,
];
$note_polygons = $this
->t('Applies to markers. If you want tooltips for lines and polygons too, please use this selector in combination with <a href="@url_leaflet_label">Leaflet Label</a>.', [
'@url_leaflet_label' => 'http://drupal.org/project/leaflet_label',
]);
$form['tooltips']['marker_tooltip'] = [
'#title' => $this
->t('Views field to populate tooltips'),
'#type' => 'select',
'#default_value' => $this->options['tooltips']['marker_tooltip'],
'#options' => $fields,
'#description' => $this
->t('Example: "Content: Title"') . '<br/>' . $note_polygons,
];
}
private function addCheckBoxes(&$form, $lib_markercluster, &$weight) {
$form['full_screen'] = [
'#title' => $this
->t('Add a full-screen toggle to the map'),
'#type' => 'checkbox',
'#default_value' => $this->options['full_screen'],
'#weight' => $weight++,
];
$form['scale_imperial'] = [
'#title' => $this
->t('Add an imperial (miles) scale'),
'#type' => 'checkbox',
'#default_value' => $this->options['scale_imperial'],
'#weight' => $weight++,
];
$form['scale_metric'] = [
'#title' => $this
->t('Add a metric (km) scale'),
'#type' => 'checkbox',
'#default_value' => $this->options['scale_metric'],
'#weight' => $weight++,
];
$form['zoom_indicator'] = [
'#title' => $this
->t('Add an indicator showing the active zoom level'),
'#type' => 'checkbox',
'#default_value' => $this->options['zoom_indicator'],
'#weight' => $weight++,
];
$form['map_reset'] = [
'#title' => $this
->t('Add a reset button'),
'#type' => 'checkbox',
'#default_value' => $this->options['map_reset'],
'#description' => $this
->t('This button allows the visitor to reset the map to its initial bounds (center and zoom level).'),
'#weight' => $weight++,
];
$form['map_reset_css_class'] = [
'#title' => $this
->t('CSS class to apply to reset button'),
'#type' => 'textfield',
'#size' => 40,
'#default_value' => $this->options['map_reset_css_class'],
'#description' => $this
->t('You can use this to superimpose a font-icon on the button. For instance, if you have the Font Awesome library loaded for your markers, try <strong>fa fa-repeat</strong>. If you enter only one or two characters, for example <strong>R</strong>, these will be used verbatim as the label instead.'),
'#weight' => $weight++,
'#states' => [
'visible' => [
'input[name="style_options[map_reset]"]' => [
'checked' => TRUE,
],
],
],
];
$form['map_cluster_toggle'] = [
'#title' => $this
->t('Add cluster toggle button'),
'#type' => 'checkbox',
'#default_value' => $this->options['map_cluster_toggle'],
'#description' => $this
->t('This button allows the visitor to toggle marker clustering on/off at any time and at any zoom level.') . '<br/>' . $this
->t('A cluster radius must be specified below.'),
'#weight' => $weight++,
];
$form['open_balloons_on_click'] = [
'#title' => $this
->t('Display non-excluded fields in a pop-up balloon above the marker, when <em>clicked</em>.'),
'#type' => 'checkbox',
'#default_value' => $this->options['open_balloons_on_click'],
'#weight' => $weight++,
];
$form['open_balloons_on_hover'] = [
'#title' => $this
->t('Display non-excluded fields in a pop-up balloon above the marker, when <em>hovered</em>.'),
'#type' => 'checkbox',
'#default_value' => $this->options['open_balloons_on_hover'],
'#weight' => $weight++,
];
$form['goto_content_on_click'] = [
'#title' => $this
->t('Go to the associated content page, when a marker is <em>clicked</em>.'),
'#type' => 'checkbox',
'#default_value' => $this->options['goto_content_on_click'],
'#weight' => $weight++,
];
if (!$lib_markercluster) {
$form['map_cluster_toggle']['#description'] .= '<br/>' . $this
->t('<a href="!url_project">Leaflet MarkerCluster</a> must be enabled.', [
'!url_project' => 'http://drupal.org/project/leaflet_markercluster',
]);
}
}
private function addSync(&$form, &$weight) {
$form['sync'] = [
'#title' => $this
->t('Cross-highlighting between map markers and page content outside the map'),
'#description' => '<br/>' . $this
->t('For the cross-highlighting to work, content outside the map must have the CSS class <em>.sync-id-[nid]</em>, where <em>[nid]</em> represents the content ID.') . ' ' . $this
->t('For Views content, you can do this by adding a <strong>Row class</strong> to the Grid, Table, HTML or Unformatted list formats of your Views Attachment or Block displays.'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => empty($this->options['sync'][LEAFLET_SYNC_CONTENT_TO_MARKER]),
'#weight' => $weight++,
];
$note = $this
->t('You can redefine this class to change the default look.');
$form['sync'][LEAFLET_SYNC_CONTENT_TO_MARKER] = [
'#title' => $this
->t('When hovering markers on the map, highlight associated content on the page'),
'#type' => 'checkbox',
'#default_value' => $this->options['sync'][LEAFLET_SYNC_CONTENT_TO_MARKER] && $this->options['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT],
'#description' => $this
->t('Content is highlighted dynamically through the automatic addition of the CSS class <em>.synced-marker-hover</em>.') . ' ' . $note,
'#weight' => $weight++,
];
$caveat = $this
->t('For this feature to work in combination with any <em>sorting</em> on the hovered content, the associated Views display must have <em>Use Ajax: No</em>.');
$form['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT] = [
'#title' => $this
->t('When hovering content, highlight associated markers on the map'),
'#type' => 'checkbox',
'#default_value' => $this->options['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT],
'#description' => $this
->t('Markers are highlighted dynamically through the automatic addition of the CSS class <em>.synced-content-hover</em>.') . ' ' . $note . '<br/>' . $caveat,
'#weight' => $weight++,
];
$form['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT_WITH_POPUP] = [
'#title' => $this
->t('As above, but also pop up marker balloons'),
'#type' => 'checkbox',
'#default_value' => $this->options['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT_WITH_POPUP],
'#states' => [
'visible' => [
':input[name="style_options[sync][4]"]' => [
'checked' => TRUE,
],
],
],
'#weight' => $weight++,
];
$form['sync'][LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT] = [
'#title' => $this
->t('Unhighlight marker and close its balloon when hovering off the map'),
'#type' => 'checkbox',
'#default_value' => $this->options['sync'][LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT],
'#states' => [
'visible' => [
':input[name="style_options[sync][4]"]' => [
'checked' => TRUE,
],
],
],
'#weight' => $weight++,
];
}
private function addMiniMapInset(&$form, &$weight) {
$form['mini_map'] = [
'#title' => $this
->t('Mini-map inset'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => !empty($this->options['mini_map']['on']),
'#description' => $this
->t('A zoomed-out version of the main map as a mini-map inset in the bottom corner.'),
'#weight' => $weight++,
];
$form['mini_map']['on'] = [
'#title' => $this
->t('Enable mini-map inset'),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['mini_map']['on']),
];
$form['mini_map']['height'] = [
'#title' => $this
->t('Height of inset'),
'#type' => 'textfield',
'#size' => 4,
'#field_suffix' => $this
->t('px'),
'#default_value' => $this->options['mini_map']['height'],
'#states' => [
'visible' => [
'input[name="style_options[mini_map][on]"]' => [
'checked' => TRUE,
],
],
],
];
$form['mini_map']['width'] = [
'#title' => $this
->t('Width of inset'),
'#type' => 'textfield',
'#size' => 4,
'#field_suffix' => $this
->t('px'),
'#default_value' => $this->options['mini_map']['width'],
'#states' => [
'visible' => [
'input[name="style_options[mini_map][on]"]' => [
'checked' => TRUE,
],
],
],
];
$form['mini_map']['toggle'] = [
'#title' => $this
->t('Allow visitor to minimise inset'),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['mini_map']['toggle']),
'#states' => [
'visible' => [
'input[name="style_options[mini_map][on]"]' => [
'checked' => TRUE,
],
],
],
];
$form['mini_map']['scope_color'] = [
'#title' => $this
->t('Scope rectangle color'),
'#type' => 'textfield',
'#size' => 10,
'#default_value' => $this->options['mini_map']['scope_color'],
'#description' => $this
->t('<em>#rrggbb</em> or <a target="_colors" href="@url">color name</a>.', [
'@url' => 'http://www.w3schools.com/html/html_colornames.asp',
]),
'#states' => [
'visible' => [
'input[name="style_options[mini_map][on]"]' => [
'checked' => TRUE,
],
],
],
];
$form['mini_map']['zoom_delta'] = [
'#title' => $this
->t('Zoom delta'),
'#type' => 'textfield',
'#size' => 3,
'#default_value' => $this->options['mini_map']['zoom_delta'],
'#description' => $this
->t('The difference between the zoom levels of main map and mini-map.'),
'#states' => [
'visible' => [
'input[name="style_options[mini_map][on]"]' => [
'checked' => TRUE,
],
],
],
];
}
private function addMarkerCluster(&$form, $lib_markercluster, &$weight) {
$desc_a = $this
->t('A typical cluster radius is 20 to 100 px. When you use a <em>cluster region differentiator</em> (see below), a marker radius of 200 px or more may give superior results. <br/>The visitor marker is excluded from clustering. Enter 0 to disable clustering altogether.');
$desc_b = $this
->t('Requires the <a target="project" href="!url_project">Leaflet MarkerCluster</a> module.', [
'!url_project' => 'http://drupal.org/project/leaflet_markercluster',
]);
$form['cluster_radius'] = [
'#title' => $this
->t('Marker cluster radius'),
'#type' => 'textfield',
'#field_suffix' => $this
->t('px'),
'#size' => 4,
'#default_value' => $this->options['cluster_radius'],
'#description' => $lib_markercluster ? $desc_a : $desc_b,
'#weight' => $weight++,
];
$form['disable_clustering_at_zoom'] = [
'#title' => $this
->t('Disable clustering at zoom'),
'#type' => 'textfield',
'#size' => 4,
'#default_value' => is_numeric($this->options['disable_clustering_at_zoom']) ? $this->options['disable_clustering_at_zoom'] : '',
'#description' => $this
->t('If you specify a zoom level, then there will be no clustering beyond that zoom level, regardless of the radius specified.'),
'#weight' => $weight++,
];
$form['allow_clusters_of_one'] = [
'#title' => $this
->t('Allow clusters of one'),
'#type' => 'checkbox',
'#default_value' => $this->options['allow_clusters_of_one'],
'#description' => $this
->t('Especially recommended when your clusters employ aggregation functions.'),
'#weight' => $weight++,
];
}
private function addClusterDifferentiator(&$form, FormStateInterface &$form_state, $lib_markercluster, &$weight) {
$path = drupal_get_path('module', 'ip_geoloc');
$intro = $this
->t('Region-aware marker clustering with <a target="regionbound" href="!url_regionbound">RegionBound</a>. See the <a target="readme" href="!url_readme">README</a> for details.', [
'!url_regionbound' => 'http://regionbound.com',
'!url_readme' => "{$path}/README.txt",
]);
$form['cluster_differentiator'] = [
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#title' => $this
->t('Cluster region differentiator'),
'#description' => '<em>' . $intro . '</em><br/>',
'#prefix' => '<div id="cluster-differentiator-wrapper">',
'#suffix' => '</div>',
'#weight' => $weight++,
];
$region_field_names = NULL;
if ($trigger = $form_state
->getTriggeringElement()) {
if (strpos($trigger['#id'], 'cluster-differentiator-field') > 0) {
$region_field_names = $trigger['#value'];
}
}
else {
$region_field_names = $this->options['cluster_differentiator']['cluster_differentiator_fields'];
}
if (!$this->moduleHandler
->moduleExists('leaflet_markercluster')) {
$desc = $this
->t('Requires the <a target="project" href="!url_project">Leaflet MarkerCluster</a> module', [
'!url_project' => 'http://drupal.org/project/leaflet_markercluster',
]);
$form['cluster_differentiator']['#description'] .= $desc . ' ' . $this
->t('and <a href="!url_regionbound">RegionBound</a> JS plugin.', [
'!url_regionbound' => 'http://regionbound.com',
]);
}
elseif ($lib_markercluster && (empty($region_field_names) || !reset($region_field_names))) {
$note_region = $this
->t('Download the file %js from <a target="regionbound" href="!url_regionbound">Regionbound</a> and drop it in %directory, without renaming. Then select your region differentiator below.', [
'%js' => IP_GEOLOC_LEAFLET_MARKERCLUSTER_REGIONBOUND_JS,
'%directory' => $lib_markercluster,
'!url_regionbound' => 'http://regionbound.com',
]);
$form['cluster_differentiator']['#description'] .= "<p>{$note_region}</p>";
}
$fields = ip_geoloc_get_display_fields($this->displayHandler, TRUE);
$form['cluster_differentiator']['cluster_differentiator_fields'] = [
'#title' => $this
->t('Region differentiator'),
'#type' => 'select',
'#multiple' => TRUE,
'#size' => 6,
'#options' => $fields,
'#default_value' => $region_field_names,
'#ajax' => [
'callback' => [
self::class,
'pluginStyleLeafletRefreshClusterFieldsetJs',
],
'wrapper' => 'cluster-differentiator-wrapper',
'prevent' => 'submit',
],
'#description' => $this
->t('Select a field (or sequence of fields) that reflect for each location marker the region hierarchy it belongs to. Examples are an <a target="drupal" href="!url_addressfield">AddressField</a>, a hierarchical taxonomy term based on regions, or individual fields for country, state, city, suburb (<em>in that order</em>). Make sure that the region differentiator you wish to use is included as a field in your view, so it appears in the list above. Note that region differentiators do <em>not</em> need to be associated with latitudes or longitudes. They are just name fields.', [
'!url_addressfield' => 'http://drupal.org/project/addressfield',
]),
];
$level = 0;
if (!empty($region_field_names)) {
foreach ($region_field_names as $region_field_name) {
if (!empty($region_field_name)) {
$region_field = FieldStorageConfig::loadByName('node', $region_field_name);
$field_type = empty($region_field
->get('type')) ? '' : $region_field
->get('type');
$region_depth = $this
->getRegionFieldDepth($region_field);
$zoom_titles = $this
->getZoomTitles($field_type, $fields[$region_field_name], $region_depth);
foreach ($zoom_titles as $title) {
$level++;
$default_value = isset($this->options['cluster_differentiator']['zoom_ranges'][$level]) ? $this->options['cluster_differentiator']['zoom_ranges'][$level] : '';
$form['cluster_differentiator']['zoom_ranges'][$level] = [
'#type' => 'textfield',
'#title' => Xss::filterAdmin($title),
'#size' => 28,
'#default_value' => $default_value,
'#element_validate' => [
'ip_geoloc_range_widget_validate',
],
];
}
}
}
if ($level > 0) {
$desc1 = $level === 1 ? $this
->t('Below enter the zoom level range to be associated with the selected differentiator.') : $this
->t('Below enter the zoom level ranges to which each of the region hierarchy levels apply.') . '<br/>' . $this
->t('Zoom ranges may start and end at any level, but must not overlap.');
$desc2 = '';
$desc3 = $this
->t('Or leave all fields blank to use the defaults and refine later.');
$zoom1 = $this
->t('Typical zoom ranges for Europe: country: 3--6, province: 7--9, city: 10--14, postcode: 15--18');
$zoom2 = $this
->t('Typical zoom ranges for US & Canada: country: 1--3, state: 4--8, city: 9--13, zip: 14--18');
$zoom3 = $this
->t('Typical zoom ranges for Australia: country: 1--2, state: 3--9, city: 10--13, suburb/postcode: 14--18');
$form['cluster_differentiator']['cluster_differentiator_fields']['#description'] = implode('<br/>', [
"{$desc1} {$desc2}<br/>{$desc3}<br/>",
$zoom1,
$zoom2,
$zoom3,
]);
}
$form['cluster_differentiator']['cluster_tooltips'] = [
'#title' => $this
->t('Add cluster tooltips'),
'#type' => 'checkbox',
'#default_value' => $this->options['cluster_differentiator']['cluster_tooltips'],
'#description' => $this
->t("When hovering a cluster, tooltips reveal the cluster region name and the names of its populated subregions."),
];
$form['cluster_differentiator']['cluster_touch_mode'] = [
'#title' => $this
->t('Cluster action on touch devices (e.g. mobile phones)'),
'#type' => 'radios',
'#options' => [
1 => $this
->t('Single tap displays cluster regions, double-tap drills into cluster (default)'),
0 => $this
->t('Single tap drills into cluster. Sub-region names not displayed, but visible on mouse devices.'),
],
'#default_value' => $this->options['cluster_differentiator']['cluster_touch_mode'],
'#description' => $this
->t('Applies only to devices that do not have a mouse, like mobile phones.'),
];
$form['cluster_differentiator']['cluster_outline'] = [
'#title' => $this
->t('Cluster population outline'),
'#type' => 'select',
'#options' => [
0 => $this
->t('Traditional (convex hull)'),
1 => $this
->t('Avant-garde (heuristic hull)'),
],
'#default_value' => $this->options['cluster_differentiator']['cluster_outline'],
'#description' => $this
->t('When hovering a cluster, a <a target="regionbound" href="!url_regionbound">coverage outline</a> visualises the envelope or footprint of the underlying marker population. Select your preferred style of doing this.', [
'!url_regionbound' => 'http://regionbound.com/enhanced-cluster-envelope-using-heuristic-hull',
]),
];
}
$intro = $this
->t('Use clusters to report on region aggregates; requires <a target="regionbound" href="!url_regionbound">RegionBound</a>', [
'!url_regionbound' => 'http://regionbound.com/coffee-prices-across-melbourne',
]);
$desc = $this
->t('This feature aggregates values of a selected field across every region and displays the resulting <em>sum/average/min/max</em> on each cluster icon at every zoom level. It also colours each cluster icon based on its aggregated value, rather than its marker count.');
$form['cluster_aggregation'] = [
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#title' => $this
->t('Cluster aggregation'),
'#description' => "<em>{$intro}</em><p>{$desc}</p>",
'#weight' => $weight++,
];
$form['cluster_aggregation']['aggregation_field'] = [
'#title' => $this
->t('Field to perform aggregation on'),
'#type' => 'select',
'#default_value' => $this->options['cluster_aggregation']['aggregation_field'],
'#options' => $fields,
'#description' => $this
->t('For aggregation to make sense, the selected field must be numeric or represent a list. If a list, aggregation will be applied to the element <em>count</em>.'),
];
$form['cluster_aggregation']['aggregation_function'] = [
'#title' => $this
->t('Aggregation function'),
'#type' => 'select',
'#default_value' => $this->options['cluster_aggregation']['aggregation_function'],
'#options' => [
'average' => $this
->t('Average'),
'maximum' => $this
->t('Maximum'),
'minimum' => $this
->t('Minimum'),
'sum' => $this
->t('Sum'),
],
];
$form['cluster_aggregation']['ranges'] = [
'#type' => 'item',
'#description' => $this
->t('Edit the above to change the color-coding of cluster icons based on their aggregate values.'),
'#prefix' => '<div id="cluster-aggregation-aggregate">',
'#suffix' => '</div>',
];
foreach ([
'small',
'medium',
'large',
] as $size) {
$default_value = isset($this->options['cluster_aggregation']['ranges'][$size]) ? $this->options['cluster_aggregation']['ranges'][$size] : '';
$form['cluster_aggregation']['ranges'][$size] = [
'#title' => "{$size} " . $this
->t('goes to'),
'#type' => 'textfield',
'#size' => 8,
'#default_value' => $default_value,
];
}
$form['cluster_aggregation']['precision'] = [
'#title' => $this
->t('Precision'),
'#type' => 'textfield',
'#size' => 2,
'#default_value' => $this->options['cluster_aggregation']['precision'],
'#description' => $this
->t('Number of significant digits used to display the aggregate value on the cluster icon. Leave blank for native formatting.'),
];
}
private function addMoreMapOptions(&$form, &$weight) {
$selected_map = $this
->pluginStyleLeafletMapGetInfo($this->options['map']);
$zoom_top = 21;
if (isset($selected_map['settings']['maxZoom'])) {
$zoom_top = $selected_map['settings']['maxZoom'];
}
$form['map_options_leaflet'] = [
'#title' => $this
->t('More map options'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => $weight++,
];
$form['map_options_leaflet']['maxzoom'] = [
'#title' => $this
->t('Maximum zoom level (0..@zoomtop)', [
'@zoomtop' => $zoom_top,
]),
'#type' => 'textfield',
'#size' => 2,
'#default_value' => $this->options['map_options_leaflet']['maxzoom'],
'#description' => $this
->t('Note that not all maps support all zoom levels.'),
];
$initial_zoom_max = $zoom_top;
if (is_numeric($this->options['map_options_leaflet']['maxzoom'])) {
$initial_zoom_max = min($zoom_top, $this->options['map_options_leaflet']['maxzoom']);
}
$form['map_options_leaflet']['zoom'] = [
'#title' => $this
->t('Initial zoom level (0..@maxzoom)', [
'@maxzoom' => $initial_zoom_max,
]),
'#type' => 'textfield',
'#size' => 2,
'#default_value' => $this->options['map_options_leaflet']['zoom'],
'#description' => $this
->t('Does not apply to auto-box centering except when only one or no markers are shown.'),
];
$form['map_options_leaflet']['zoom_on_click'] = [
'#title' => $this
->t('Zoom-on-click zoom level (1..@maxzoom)', [
'@maxzoom' => $zoom_top,
]),
'#type' => 'textfield',
'#size' => 2,
'#default_value' => $this->options['map_options_leaflet']['zoom_on_click'],
'#description' => $this
->t('Level to zoom to when a marker is clicked. Leave blank to disable this feature.'),
];
$form['map_options_leaflet']['center_lat'] = [
'#title' => $this
->t('Latitude of initial center of map'),
'#type' => 'textfield',
'#size' => 6,
'#default_value' => $this->options['map_options_leaflet']['center_lat'],
'#description' => $this
->t('If both latitude and longitude are filled out, these override any centering option until the visitor changes their location.'),
];
$form['map_options_leaflet']['center_lon'] = [
'#title' => $this
->t('Longitude of initial center of map'),
'#type' => 'textfield',
'#size' => 6,
'#default_value' => $this->options['map_options_leaflet']['center_lon'],
'#description' => $this
->t('If both latitude and longitude are filled out, these override any centering option until the visitor changes their location.'),
];
$form['map_options_leaflet']['scrollwheelzoom'] = [
'#title' => $this
->t('Enable scroll wheel zoom'),
'#type' => 'select',
'#default_value' => $this->options['map_options_leaflet']['scrollwheelzoom'],
'#options' => [
TRUE => $this
->t('Yes'),
FALSE => $this
->t('No'),
],
];
$form['map_options_leaflet']['dragging'] = [
'#title' => $this
->t('Dragging/Panning of the map'),
'#type' => 'select',
'#default_value' => $this->options['map_options_leaflet']['dragging'],
'#options' => [
TRUE => $this
->t('Yes'),
FALSE => $this
->t('No'),
],
];
$form['map_options_leaflet']['separator'] = [
'#title' => $this
->t('Separator used in marker balloons'),
'#type' => 'textfield',
'#size' => 10,
'#default_value' => $this->options['map_options_leaflet']['separator'],
'#description' => $this
->t('You may use most HTML tags.'),
];
}
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
$this->viewPluginStyle
->pluginStyleBulkOfFormValidate($form, $form_state);
$this_options = $form_state
->getValue('style_options');
$map_height = trim($this_options['map_height']);
if (is_numeric($map_height) && $map_height <= 0) {
form_error($form['map_height'], $this
->t('Map height must be a positive number.'));
}
$selected_map = $this
->pluginStyleLeafletMapGetInfo($this_options['map']);
$zoom_top = 18;
if (isset($selected_map['settings']['maxZoom'])) {
$zoom_top = $selected_map['settings']['maxZoom'];
}
$max_zoom = $this_options['map_options_leaflet']['maxzoom'];
if ($max_zoom != '' && (!is_numeric($max_zoom) || $max_zoom < 0 || $max_zoom > $zoom_top)) {
$form_state
->setError($form['map_options_leaflet']['maxzoom'], $this
->t('"Maximum zoom level" for %map must be in range 0..@zoomtop', [
'%map' => $selected_map['label'],
'@zoomtop' => $zoom_top,
]));
}
$zoom = $this_options['map_options_leaflet']['zoom'];
if ($zoom != '' && (!is_numeric($zoom) || $zoom < 0 || $zoom > $max_zoom)) {
$form_state
->setError($form['map_options_leaflet']['zoom'], $this
->t('"Initial zoom level" must be a non-negative number not greater than "Maximum zoom level".'));
}
$disable_zoom = $this_options['disable_clustering_at_zoom'];
if ($disable_zoom != '' && (!is_numeric($disable_zoom) || $disable_zoom < 0 || $disable_zoom > $max_zoom)) {
$form_state
->setError($form['disable_clustering_at_zoom'], $this
->t('"Disable clustering at zoom" level must be a positive number not greater than "Maximum zoom level".'));
}
if (isset($this_options['cluster_aggregation'])) {
$cluster_aggregation = $this_options['cluster_aggregation'];
if (!empty($cluster_aggregation['aggregation_field'])) {
$aggregation_field_info = FieldStorageConfig::loadByName($cluster_aggregation['aggregation_field']);
$valid_types = [
'number_integer',
'number_decimal',
'number_float',
'entityreference',
];
if (!$aggregation_field_info || !in_array($aggregation_field_info['type'], $valid_types)) {
$this->messenger
->addMessage($this
->t('A cluster aggregation field that cannot be interpreted as a number may cause unexpected errors.'));
}
}
}
}
public function getCacheMaxAge() {
return 0;
}
public function getRegionFieldDepth($region_field) {
$type = $region_field
->get('type');
if (empty($type)) {
return 1;
}
if ($type === 'addressfield') {
return 4;
}
$settings = $region_field
->getSettings();
if ($settings['target_type'] != 'taxonomy_term') {
return 1;
}
$depth = 0;
return $depth + 1;
}
private function getZoomTitles($field_type, $label, $region_depth) {
$titles = [];
if ($field_type === 'addressfield') {
$titles[] = $this
->t('Zoom range for country');
$titles[] = $this
->t('Zoom range for administrative area (state, district, county)');
$titles[] = $this
->t('Zoom range for locality (city, town, village)');
$titles[] = $this
->t('Zoom range for post code (ZIP)');
return $titles;
}
$pos_colon = strrpos($label, ':');
$label = Unicode::substr($label, $pos_colon > 0 ? $pos_colon + 1 : 0);
if ($field_type === 'taxonomy_term_reference') {
for ($level = 1; $level <= $region_depth; $level++) {
$titles[] = $this
->t('Zoom range for %field level @level', [
'%field' => $label,
'@level' => $level,
]);
}
}
else {
$titles[] = $this
->t('Zoom range for %field', [
'%field' => $label,
]);
}
return $titles;
}
private function fillOutLocationRegion($field_type, &$location, $region, &$level) {
switch ($field_type) {
case 'taxonomy_term_reference':
$region_hierarchy = taxonomy_get_parents_all($region);
foreach (array_reverse($region_hierarchy) as $region_term) {
$location->regions[$level++] = trim($region_term->name);
}
break;
case 'addressfield':
if (!empty($region)) {
$format_callback = 'addressfield_format_address_generate';
if (function_exists($format_callback) && isset($region['country'])) {
$format = [];
$context = [
'mode' => NULL,
];
addressfield_format_address_generate($format, $region, $context);
if (isset($format['country']['#options'][$region['country']])) {
$region['country'] = $format['country']['#options'][$region['country']];
}
if (isset($region['administrative_area']) && isset($format['locality_block']['administrative_area']['#options'][$region['administrative_area']])) {
$region['administrative_area'] = $format['locality_block']['administrative_area']['#options'][$region['administrative_area']];
}
}
else {
}
$location->regions = [
1 => isset($region['country']) ? trim($region['country']) : '',
2 => isset($region['administrative_area']) ? trim($region['administrative_area']) : '',
3 => isset($region['locality']) ? trim($region['locality']) : '',
4 => isset($region['postal_code']) ? trim($region['postal_code']) : '',
];
$level = 5;
}
break;
default:
$location->regions[$level++] = trim($region);
}
}
public function fillOutLocationRegions($locations) {
if (empty($this->options['cluster_differentiator']['cluster_differentiator_fields'])) {
return;
}
$region_fields = [];
foreach ($this->options['cluster_differentiator']['cluster_differentiator_fields'] as $region_fieldname) {
$region_field = FieldStorageConfig::loadByName('node', $region_fieldname);
$region_fields[] = empty($region_field) ? $region_fieldname : $region_field;
}
if (empty($region_fields) || !reset($region_fields)) {
return;
}
foreach ($this->view->result as $key => $row) {
if (isset($locations[$key])) {
$level = 1;
foreach ($region_fields as $region_field) {
$region_values = $this->viewPluginStyle
->getViewResult($this, $region_field, $key);
$field_type = $region_field
->getType();
$region = $field_type == 'addressfield' ? $region_values : end($region_values);
if (empty($region)) {
$region = '';
}
$this
->fillOutLocationRegion($field_type, $locations[$key], $region, $level);
}
}
}
}
public function getCenter($location) {
if (empty($location->type) || $location->type == 'point') {
$lat = isset($location->lat) ? $location->lat : (isset($location->latitude) ? $location->latitude : 0.0);
$lon = isset($location->lon) ? $location->lon : (isset($location->longitude) ? $location->longitude : 0.0);
return [
'lat' => $lat,
'lon' => $lon,
];
}
if (!empty($location->component[0]['points'][0])) {
return $location->component[0]['points'][0];
}
}
public function isNoMarker($marker_color) {
return isset($marker_color) && ($marker_color === '0' || $marker_color === 0 || $marker_color === FALSE);
}
public function pluginStyleLeafletMapGetInfo($map_name = NULL) {
return $this->moduleHandler
->moduleExists('leaflet') ? leaflet_map_get_info($map_name) : [];
}
public function pluginStyleLeafletCompare($location1, $location2) {
$weight1 = empty($location1->weight) ? 0 : $location1->weight;
$weight2 = empty($location2->weight) ? 0 : $location2->weight;
return $weight2 - $weight1;
}
public function pluginStyleLeafletRefreshClusterFieldsetJs($form, &$form_state) {
return $form['options']['style_options']['cluster_differentiator'];
}
public function render() {
if (empty($this->options['map']) || !($map = $this
->pluginStyleLeafletMapGetInfo($this->options['map']))) {
return $this
->t('No Leaflet map was selected or map configuration was not found.');
}
if (!empty($this->view->preview)) {
return $this
->t('The preview function is incompatible with Leaflet maps so cannot be used. Please visit the page path or the block to view your map.');
}
$render_start = microtime(TRUE);
$this->viewPluginStyle
->pluginStyleRenderFields($this);
$open_balloons_on_click = !empty($this->options['open_balloons_on_click']);
$open_balloons_on_hover = !empty($this->options['open_balloons_on_hover']);
$enable_balloons = $open_balloons_on_click || $open_balloons_on_hover;
$locations = $this->viewPluginStyle
->pluginStyleExtractLocations($this, $enable_balloons);
$this
->fillOutLocationRegions($locations);
$marker_color = $this->options['default_marker']['default_marker_color'];
$visitor_marker_color = $this->options['visitor_marker_leaflet']['visitor_marker_color'];
$center_option = !isset($this->options['center_option']) ? 0 : $this->options['center_option'];
$sync_flags = 0;
if (!empty($this->options['sync'][LEAFLET_SYNC_CONTENT_TO_MARKER])) {
$sync_flags |= LEAFLET_SYNC_CONTENT_TO_MARKER;
}
if (!empty($this->options['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT])) {
$sync_flags |= LEAFLET_SYNC_MARKER_TO_CONTENT;
if (!empty($this->options['sync'][LEAFLET_SYNC_MARKER_TO_CONTENT_WITH_POPUP])) {
$sync_flags |= LEAFLET_SYNC_MARKER_TO_CONTENT_WITH_POPUP;
}
if (!empty($this->options['sync'][LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT])) {
$sync_flags |= LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT;
}
}
$has_full_screen = !empty($this->options['full_screen']);
$has_mini_map = !empty($this->options['mini_map']['on']);
$zoom_indicator = empty($this->options['zoom_indicator']) ? FALSE : TRUE;
$scale_control = FALSE;
if (!empty($this->options['scale_metric']) || !empty($this->options['scale_imperial'])) {
$scale_control = [
'metric' => !empty($this->options['scale_metric']),
'imperial' => !empty($this->options['scale_imperial']),
];
}
$goto_content_on_click = !empty($this->options['goto_content_on_click']);
$reset_control = FALSE;
if (!empty($this->options['map_reset'])) {
$label = Xss::filterAdmin($this->options['map_reset_css_class']);
$reset_control = [
'label' => empty($label) ? ' ' : $label,
];
}
$cluster_control = FALSE;
if ($this->moduleHandler
->moduleExists('leaflet_markercluster') && !empty($this->options['map_cluster_toggle'])) {
$cluster_control = [
'label' => 'C',
];
}
$cluster_radius = (int) $this->session
->getSessionValue('markercluster-radius');
if ($cluster_radius < 2) {
$cluster_radius = (int) $this->options['cluster_radius'];
}
$disable_clustering_at_zoom = $this->options['disable_clustering_at_zoom'];
$hull_hug_factor = empty($this->options['cluster_differentiator']['cluster_outline']) ? -1 : 'auto';
$cluster_tooltips = !empty($this->options['cluster_differentiator']['cluster_tooltips']);
$cluster_touch_mode = empty($this->options['cluster_differentiator']['cluster_touch_mode']) ? 0 : 'auto';
$cluster_aggregation_field = $this->options['cluster_aggregation']['aggregation_field'];
$cluster_aggregation_function = $this->options['cluster_aggregation']['aggregation_function'];
$cluster_aggregate_ranges = $this->options['cluster_aggregation']['ranges'];
$cluster_aggregate_precision = $this->options['cluster_aggregation']['precision'];
$allow_clusters_of_one = !empty($this->options['allow_clusters_of_one']);
$tag_css_classes = $this->options['tags']['tag_css_class'];
$module_path = drupal_get_path('module', 'ip_geoloc');
$marker_path = file_create_url($this->ipGeolocGlobal
->markerDirectory());
$max_zoom = (int) $this->options['map_options_leaflet']['maxzoom'];
$zoom = max(1, (int) $this->options['map_options_leaflet']['zoom']);
$zoom_on_click = (int) $this->options['map_options_leaflet']['zoom_on_click'];
$scroll_wheel_zoom = (bool) $this->options['map_options_leaflet']['scrollwheelzoom'];
$dragging = (bool) $this->options['map_options_leaflet']['dragging'];
$visitor_location = $this->api
->getVisitorLocation();
if (!isset($visitor_location['latitude'])) {
$connection = \Drupal::database();
$ip = \Drupal::request()
->getClientIp();
$result = $connection
->query('SELECT * FROM {ip_geoloc} WHERE ip_address = :ip_address', [
':ip_address' => $ip,
], []);
foreach ($result as $item) {
$visitor_location = $item;
}
}
$use_specified_center = !empty($this->options['map_options_leaflet']['center_lat']) && !empty($this->options['map_options_leaflet']['center_lon']) && empty($visitor_location['is_updated']);
if ($use_specified_center) {
$map['center'] = [
'lat' => $this->options['map_options_leaflet']['center_lat'],
'lon' => $this->options['map_options_leaflet']['center_lon'],
];
}
elseif (!empty($locations) && ($center_option == IP_GEOLOC_MAP_CENTER_ON_FIRST_LOCATION || $visitor_marker_color == 'none' && count($locations) == 1)) {
$map['center'] = $this
->getCenter(reset($locations));
}
elseif (($center_option == IP_GEOLOC_MAP_CENTER_OF_LOCATIONS || $center_option == IP_GEOLOC_MAP_CENTER_OF_LOCATIONS_WEIGHTED) && !empty($locations)) {
list($center_lat, $center_lon) = $this->api
->centerOfLocations($locations, $center_option == IP_GEOLOC_MAP_CENTER_OF_LOCATIONS_WEIGHTED);
$map['center'] = [
'lat' => $center_lat,
'lon' => $center_lon,
];
}
if (!$use_specified_center && (empty($locations) || $center_option == IP_GEOLOC_MAP_CENTER_ON_VISITOR) && isset($visitor_location['latitude'])) {
$map['center'] = [
'lat' => $visitor_location['latitude'],
'lon' => $visitor_location['longitude'],
];
}
if (empty($locations)) {
$ll = trim($this->options['empty_map_center']);
if (empty($ll)) {
return;
}
if ($ll != $this
->t('visitor')) {
list($map['center']['lat'], $map['center']['lon']) = preg_split("/[\\s,]+/", $ll);
}
}
else {
uasort($locations, '_ip_geoloc_plugin_style_leaflet_compare');
}
$marker_dimensions = explode('x', $this->ipGeolocGlobal
->markerDimensions());
$marker_width = (int) $marker_dimensions[0];
$marker_height = (int) $marker_dimensions[1];
$anchor_position = $this->config
->get('ip_geoloc_marker_anchor_pos') ? $this->config
->get('ip_geoloc_marker_anchor_pos') : 'bottom';
switch ($anchor_position) {
case 'top':
$marker_anchor = 0;
break;
case 'middle':
$marker_anchor = (int) (($marker_height + 1) / 2);
break;
default:
$marker_anchor = $marker_height;
}
$features = [];
foreach ($locations as $location) {
$feature = [];
if (isset($location->latitude) || isset($location->lat)) {
$feature['type'] = 'point';
$feature['lat'] = isset($location->latitude) ? $location->latitude : $location->lat;
$feature['lon'] = isset($location->longitude) ? $location->longitude : $location->lon;
if (!empty($location->random_displacement)) {
$this->api
->addRandomDisplacement($feature, $location->random_displacement);
$circle = [
'type' => 'circle',
'lat' => $feature['lat'],
'lon' => $feature['lon'],
'radius' => $location->random_displacement,
];
$features[] = $circle;
}
}
elseif (isset($location->component)) {
$feature['type'] = $location->type;
$feature['component'] = $location->component;
}
elseif (isset($location->points)) {
$feature['type'] = $location->type;
$feature['points'] = $location->points;
}
if (isset($location->id)) {
$feature['feature_id'] = $location->id;
}
if (!empty($feature['type']) && $feature['type'] != 'point') {
$feature['flags'] = LEAFLET_MARKERCLUSTER_EXCLUDE_FROM_CLUSTER;
}
elseif (!isset($feature['lat'])) {
continue;
}
if (!empty($sync_flags)) {
$feature['flags'] = isset($feature['flags']) ? $feature['flags'] | $sync_flags : $sync_flags;
}
if ($enable_balloons && isset($location->balloon_text)) {
$feature['popup'] = $location->balloon_text;
}
if (!empty($location->marker_special_char) || !empty($location->marker_special_char_class)) {
$has_special_markers = TRUE;
if (!empty($location->marker_special_char)) {
$feature['specialChar'] = Xss::filterAdmin($location->marker_special_char);
}
if (!empty($location->marker_special_char_class)) {
$feature['specialCharClass'] = Xss::filterAdmin($location->marker_special_char_class);
}
}
elseif (!empty($this->options['default_marker']['default_marker_special_char']) || !empty($this->options['default_marker']['default_marker_special_char_class'])) {
$has_special_markers = TRUE;
$feature['specialChar'] = $this->options['default_marker']['default_marker_special_char'];
$feature['specialCharClass'] = $this->options['default_marker']['default_marker_special_char_class'];
}
if (!empty($location->marker_tooltip)) {
if ($this->moduleHandler
->moduleExists('leaflet_label')) {
$feature['label'] = $location->marker_tooltip;
}
else {
$has_special_markers = TRUE;
$feature['tooltip'] = $location->marker_tooltip;
}
}
if (!empty($location->regions)) {
$has_special_markers = TRUE;
$feature['regions'] = [
0 => '',
] + $location->regions;
if (isset($location->aggregation_value)) {
$feature['aggregationValue'] = (double) $location->aggregation_value;
}
$feature['tooltip'] = empty($feature['tooltip']) ? '' : $feature['tooltip'] . "\n";
$second_last = count($feature['regions']) - 2;
if ($second_last > 0) {
$feature['tooltip'] .= $feature['regions'][$second_last] . ' - ';
}
$feature['tooltip'] .= end($feature['regions']);
}
if (!empty($location->marker_tag)) {
$has_special_markers = TRUE;
$feature['tag'] = $location->marker_tag;
}
if (!empty($tag_css_classes)) {
$feature['cssClass'] = $tag_css_classes;
}
if (isset($location->marker_color) && $this
->isNoMarker($location->marker_color) || !isset($location->marker_color) && $this
->isNoMarker($marker_color)) {
$has_special_markers = TRUE;
$feature['icon'] = FALSE;
}
elseif (!empty($location->marker_color) || !empty($marker_color)) {
$color = empty($location->marker_color) ? $marker_color : $location->marker_color;
$feature['icon'] = [
'iconUrl' => $marker_path . "/{$color}.png",
'iconSize' => [
'x' => $marker_width,
'y' => $marker_height,
],
'iconAnchor' => [
'x' => (int) (($marker_width + 1) / 2),
'y' => $marker_anchor,
],
'popupAnchor' => [
'x' => 0,
'y' => -$marker_height - 1,
],
];
}
$features[] = $feature;
}
if (isset($visitor_location['latitude'])) {
if ($visitor_marker_color != 'none') {
$visitor_feature = [
'type' => 'point',
'lat' => $visitor_location['latitude'],
'lon' => $visitor_location['longitude'],
'specialChar' => Xss::filterAdmin($this->options['visitor_marker_leaflet']['visitor_marker_special_char']),
'specialCharClass' => Xss::filterAdmin($this->options['visitor_marker_leaflet']['visitor_marker_special_char_class']),
'popup' => !empty($visitor_location['popup']) ? $visitor_location['popup'] : $this
->t('Your approximate location'),
'tooltip' => !empty($visitor_location['tooltip']) ? $visitor_location['tooltip'] : $this
->t('Your approximate location'),
'zIndex' => 9999,
'flags' => LEAFLET_MARKERCLUSTER_EXCLUDE_FROM_CLUSTER,
];
if ($visitor_marker_color != '') {
if (!empty($visitor_feature['specialChar']) || !empty($visitor_feature['specialCharClass'])) {
$has_special_markers = TRUE;
}
$visitor_feature['icon'] = [
'iconUrl' => $marker_path . "/{$visitor_marker_color}.png",
'iconSize' => [
'x' => $marker_width,
'y' => $marker_height,
],
'iconAnchor' => [
'x' => (int) (($marker_width + 1) / 2),
'y' => $marker_anchor,
],
'popupAnchor' => [
'x' => 0,
'y' => -$marker_height - 1,
],
];
}
$features[] = $visitor_feature;
}
if (!empty($this->options['visitor_marker_leaflet']['visitor_marker_accuracy_circle']) && !empty($visitor_location['accuracy'])) {
$visitor_accuracy_circle = [
'type' => 'circle',
'lat' => $visitor_location['latitude'],
'lon' => $visitor_location['longitude'],
'radius' => (double) $visitor_location['accuracy'],
'popup' => !empty($visitor_location['popup']) ? $visitor_location['popup'] : $this
->t("You are within @m meters of the centre of this circle.", [
'@m' => $visitor_location['accuracy'],
]),
'label' => !empty($visitor_location['tooltip']) ? $visitor_location['tooltip'] : $this
->t('You are within this circle'),
'zIndex' => 9998,
'flags' => LEAFLET_MARKERCLUSTER_EXCLUDE_FROM_CLUSTER,
];
$features[] = $visitor_accuracy_circle;
}
}
if (!$use_specified_center && empty($center_option) && count($features) > 1) {
unset($map['center']);
}
else {
$map['settings']['zoom'] = $zoom;
if (!empty($map['center'])) {
$map['center']['force'] = TRUE;
}
}
$map['settings']['maxZoom'] = $max_zoom;
$map['settings']['scrollWheelZoom'] = $scroll_wheel_zoom;
$map['settings']['dragging'] = $dragging;
$map['settings']['revertLastMarkerOnMapOut'] = (bool) ($sync_flags & LEAFLET_SYNC_REVERT_LAST_MARKER_ON_MAP_OUT);
$map['settings']['maxClusterRadius'] = 0;
if ($cluster_radius > 0) {
if ($this->moduleHandler
->moduleExists('leaflet_markercluster')) {
$map['settings']['maxClusterRadius'] = $cluster_radius;
$map['settings']['disableClusteringAtZoom'] = $disable_clustering_at_zoom;
$map['settings']['addRegionToolTips'] = $cluster_tooltips;
$map['settings']['hullHugFactor'] = $hull_hug_factor;
$map['settings']['touchMode'] = $cluster_touch_mode;
$map['settings']['animateAddingMarkers'] = TRUE;
if (!empty($cluster_aggregation_field)) {
$map['settings']['clusterAggregationFunction'] = $cluster_aggregation_function;
$map['settings']['clusterAggregateRanges'] = $cluster_aggregate_ranges;
$map['settings']['clusterAggregatePrecision'] = $cluster_aggregate_precision;
$output['#attached']['library'][] = 'leaflet/markercluster-aggregation';
}
if ($allow_clusters_of_one) {
$map['settings']['allowClustersOfOne'] = TRUE;
$map['settings']['spiderfyDistanceMultiplier'] = 4.0;
}
}
else {
$display = $this->view
->getDisplay();
$display_name = $display
->getOption('title') . ' (' . $display->display['display_title'] . ')';
$this->messenger
->addMessage($this
->t('Cannot cluster markers in View %display_name, as the module Leaflet MarkerCluster is not enabled.', [
'%display_name' => $display_name,
]), 'warning');
}
}
$zoom_ranges = array_filter($this->options['cluster_differentiator']['zoom_ranges']);
if (!empty($zoom_ranges)) {
$region_levels = array_fill(0, $max_zoom + 1, 0);
foreach ($zoom_ranges as $level => $zoom_range) {
for ($zoom = 1; $zoom <= $max_zoom; $zoom++) {
if ($this->ip_geoloc_global
->isInRange($zoom, $zoom_range)) {
$region_levels[$zoom] = $level;
}
}
}
for ($z = 1; $z <= $max_zoom; $z++) {
if (empty($region_levels[$z])) {
$region_levels[$z] = $region_levels[$z - 1];
}
}
$map['settings']['regionLevels'] = $region_levels;
}
$map_id = 'ip-geoloc-map-of-view-' . $this->view->id . '-' . $this->view
->getDisplay()->display['display_id'] . '-' . md5(serialize($features));
$output['#attached']['library'][] = 'leaflet/leaflet-drupal';
if ($sync_flags !== 0) {
$output['#attached']['library'][] = 'ip_geoloc/sync_content';
}
if ($has_full_screen) {
$output['#attached']['library'][] = 'ip_geoloc/leaflet-fullscreen';
$map['settings']['fullscreenControl'] = [
'position' => 'topright',
];
}
if ($has_mini_map) {
$output['#attached']['library'][] = 'ip_geoloc/leaflet-minimap';
$map['settings']['miniMap'] = [
'autoToggleDisplay' => TRUE,
'height' => $this->options['mini_map']['height'],
'width' => $this->options['mini_map']['width'],
'position' => 'bottomright',
'toggleDisplay' => !empty($this->options['mini_map']['toggle']),
'zoomAnimation' => FALSE,
'zoomLevelOffset' => (int) $this->options['mini_map']['zoom_delta'],
'aimingRectOptions' => [
'color' => $this->options['mini_map']['scope_color'],
'weight' => 3,
'fillOpacity' => 0.1,
],
'shadowRectOptions' => [
'color' => '#888',
'weight' => 1,
],
];
}
$map['settings']['zoomIndicator'] = $zoom_indicator;
$map['settings']['zoomOnClick'] = $zoom_on_click;
$map['settings']['resetControl'] = $reset_control;
$map['settings']['clusterControl'] = $cluster_control;
$map['settings']['scaleControl'] = $scale_control;
if ($has_mini_map || $zoom_indicator || $reset_control || $cluster_control || $scale_control) {
$output['#attached']['library'][] = 'ip_geoloc/leaflet_controls';
}
$map['settings']['openBalloonsOnHover'] = $open_balloons_on_hover;
$map['settings']['gotoContentOnClick'] = $goto_content_on_click;
if ($open_balloons_on_hover || $goto_content_on_click) {
$output['#attached']['library'][] = 'ip_geoloc/content_on_click';
}
$settings = [
'mapid' => $map_id,
'map' => $map,
'features' => $features,
];
$output['#attached']['drupalSettings']['leaflet'] = array(
$settings,
);
$output['#attached']['library'][] = 'leaflet/leaflet';
if ($reset_control || $cluster_control || !empty($has_special_markers)) {
$output['#attached']['library'][] = 'ip_geoloc/ip_geoloc_font_icon_libs';
$variables['#attached']['library'][] = 'ip_geoloc/leaflet_markers';
}
if ($zoom_on_click) {
$variables['#attached']['library'][] = 'ip_geoloc/leaflet_zoom_on_click';
}
$marker_set = $this->ipGeolocGlobal
->markerDirectory();
$marker_set = Unicode::substr($marker_set, strrpos($marker_set, '/') + 1);
$height = trim($this->options['map_height']) ? trim($this->options['map_height']) : '300';
$height = empty($height) ? '300px' : (is_numeric($height) ? $height . 'px' : trim($height));
$style = Unicode::substr($height, 0, 6) == '<none>' ? '' : ' style="height:' . SafeMarkup::checkPlain($height) . '"';
$output = $output + [
'#theme' => 'ip_geoloc_leaflet',
'#map_id' => $map_id,
'#marker_set' => $marker_set,
'#style' => $style,
];
$this->ipGeolocGlobal
->debug($this
->t('-- Leaflet map preparation time: %sec s', [
'%sec' => number_format(microtime(TRUE) - $render_start, 2),
]));
return \Drupal::service('renderer')
->render($output);
}
}