public function IpGeoLocPluginStyleLeaflet::render in IP Geolocation Views & Maps 8
Transform the View result in a list of marker locations and render on map.
@todo refactor
Overrides StylePluginBase::render
File
- src/
Plugin/ views/ style/ IpGeoLocPluginStyleLeaflet.php, line 1334
Class
- IpGeoLocPluginStyleLeaflet
- Views Style plugin extension for Leaflet (if enabled).
Namespace
Drupal\ip_geoloc\Plugin\views\styleCode
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.');
}
// @TODO check if this is the right property
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;
/*array('position' => 'topleft')*/
$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();
//$query = $connection->select('ip_geoloc')->condition('ip_address', $ip);
$result = $connection
->query('SELECT * FROM {ip_geoloc} WHERE ip_address = :ip_address', [
':ip_address' => $ip,
], []);
foreach ($result as $item) {
$visitor_location = $item;
}
// $visitor_location = $query->execute();
}
$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)) {
// No map whatsoever.
return;
}
if ($ll != $this
->t('visitor')) {
// Empty map centered on coordinates provided.
list($map['center']['lat'], $map['center']['lon']) = preg_split("/[\\s,]+/", $ll);
}
// else: empty map centered on visitor location, as set above.
}
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)) {
// Possibly parsed by leaflet_process_geofield()
// see _ip_geoloc_plugin_style_extract_lat_lng().
$feature['type'] = $location->type;
$feature['component'] = $location->component;
}
elseif (isset($location->points)) {
$feature['type'] = $location->type;
$feature['points'] = $location->points;
}
if (isset($location->id)) {
// Allow marker events to identify the corresponding node.
$feature['feature_id'] = $location->id;
}
// At this point $feature['type'] should be set.
if (!empty($feature['type']) && $feature['type'] != 'point') {
// Linestring, polygon ...
$feature['flags'] = LEAFLET_MARKERCLUSTER_EXCLUDE_FROM_CLUSTER;
}
elseif (!isset($feature['lat'])) {
// Points must have coords.
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;
// Make sure we start with 0 or regions will come across as Object
// rather than array.
$feature['regions'] = [
0 => '',
] + $location->regions;
if (isset($location->aggregation_value)) {
$feature['aggregationValue'] = (double) $location->aggregation_value;
}
// Note: cannot use <br/> or HTML in tooltip as separator. Use \n.
$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)) {
// "No marker" as opposed to "default" marker.
$has_special_markers = TRUE;
$feature['icon'] = FALSE;
}
elseif (!empty($location->marker_color) || !empty($marker_color)) {
// Switch from default icon to specified 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,
],
// Just above topline, center.
'popupAnchor' => [
'x' => 0,
'y' => -$marker_height - 1,
],
];
}
$features[] = $feature;
}
if (isset($visitor_location['latitude'])) {
if ($visitor_marker_color != 'none') {
// See leaflet/README.txt for examples of Leaflet "features".
$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,
// See leaflet_markercluster.drupal.js.
'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,
],
// Just above topline, center.
'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'],
]),
// Requires Leaflet Label.
'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 auto-box is chosen ($center_option==0), zoom only when there are
// 0 or 1 markers [#1863374].
if (!$use_specified_center && empty($center_option) && count($features) > 1) {
unset($map['center']);
}
else {
$map['settings']['zoom'] = $zoom;
if (!empty($map['center'])) {
// A leaflet.drupal.js quirk? Have to specify AND force a 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;
//@TODO check where is this library
$output['#attached']['library'][] = 'leaflet/markercluster-aggregation';
//drupal_add_css(leaflet_markercluster_get_library_path() . '/MarkerCluster.Aggregations.css');
}
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)) {
// Make sure we start array with 0 and no missing elements. Otherwise this
// array will arrive as an Object on the JS side.
$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;
}
}
}
// Remove any gaps and zeroes.
for ($z = 1; $z <= $max_zoom; $z++) {
if (empty($region_levels[$z])) {
$region_levels[$z] = $region_levels[$z - 1];
}
}
$map['settings']['regionLevels'] = $region_levels;
}
// @TODO Check this code for displayid
$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';
// Don't load sync JS and CSS when option is not requested.
if ($sync_flags !== 0) {
$output['#attached']['library'][] = 'ip_geoloc/sync_content';
}
if ($has_full_screen) {
// Load the 'leaflet-fullscreen' library, containing JS and CSS.
// @TODO check libraries integration
//@TODO check where is this library
$output['#attached']['library'][] = 'ip_geoloc/leaflet-fullscreen';
$map['settings']['fullscreenControl'] = [
'position' => 'topright',
];
}
if ($has_mini_map) {
// Load the 'leaflet-minimap' library, containing JS and CSS.
// See https://github.com/Norkart/Leaflet-MiniMap for more settings.
// @TODO add minimap library
$output['#attached']['library'][] = 'ip_geoloc/leaflet-minimap';
/*if (drupal_add_library('ip_geoloc', 'leaflet-minimap')) {*/
$map['settings']['miniMap'] = [
'autoToggleDisplay' => TRUE,
'height' => $this->options['mini_map']['height'],
'width' => $this->options['mini_map']['width'],
'position' => 'bottomright',
// 'bottomright'
'toggleDisplay' => !empty($this->options['mini_map']['toggle']),
'zoomAnimation' => FALSE,
'zoomLevelOffset' => (int) $this->options['mini_map']['zoom_delta'],
// Superimposed rectangle showing extent of main map on the inset.
'aimingRectOptions' => [
'color' => $this->options['mini_map']['scope_color'],
'weight' => 3,
'fillOpacity' => 0.1,
],
// The "shadow" rectangle that shows the new map outline.
'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';
/*drupal_add_js($module_path . '/js/ip_geoloc_leaflet_controls.js', array('weight' => 1));
drupal_add_css($module_path . '/css/ip_geoloc_leaflet_controls.css');*/
}
$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,
);
// @TODO check how to send variales to the libray
// drupal_add_js(array('leaflet' => array($settings)), $this->options);
$output['#attached']['library'][] = 'leaflet/leaflet';
// @TODO not sure if this is migrated
// libraries_load('leaflet');
// Little hacky this, but can't see another way to load libraries for
// Leaflet More Maps, Leaflet MarkerCluster, Leaflet Hash...
// drupal_alter('leaflet_map_prebuild', $settings);
if ($reset_control || $cluster_control || !empty($has_special_markers)) {
// Load the CSS that comes with the font icon library which in return
// tells the browser to fetch either the WOFF, TTF or SVG files that
// define the font faces.
$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';
// drupal_add_js($module_path . '/js/ip_geoloc_leaflet_zoom_on_click.js', array('scope' => 'footer'));.
}
// drupal_alter('leaflet_build_map', $build); // @todo [#2567391].
$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,
//'#view' => $this->view,
'#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);
//return $output;
}