Files
freepress-map/fp-geo-content/includes/class-map-renderer.php
2026-02-13 13:14:41 +01:00

358 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Renderizador del mapa
*
* @package FP_Geo_Content
*/
if (!defined('ABSPATH')) {
exit;
}
class FP_Geo_Map_Renderer
{
/**
* Contador de instancias para IDs únicos
*/
private static $instance_count = 0;
/**
* Shortcode: [fp-geo-map]
*
* Atributos:
* - post_types: tipos de contenido separados por coma (usa config si no se especifica)
* - taxonomies: taxonomías para filtros separadas por coma
* - height: altura del mapa (default: 600px)
* - lat: latitud centro (usa config si no se especifica)
* - lng: longitud centro
* - zoom: nivel de zoom inicial
* - filters: true|false - mostrar filtros
* - detail: sidebar|modal - cómo mostrar el detalle
* - sidebar_position: left|right - posición del sidebar
* - legend: true|false - mostrar leyenda
* - show_detail_btn: true|false - mostrar botón de detalle
* - detail_btn_text: texto del botón
* - class: clases CSS adicionales
*/
public static function render_shortcode($atts)
{
$options = get_option('fp_geo_content_options', []);
$atts = shortcode_atts([
'post_types' => implode(',', $options['post_types'] ?? []),
'taxonomies' => implode(',', $options['filter_taxonomies'] ?? []),
'height' => '600px',
'lat' => $options['default_lat'] ?? '40.4168',
'lng' => $options['default_lng'] ?? '-3.7038',
'zoom' => $options['default_zoom'] ?? 12,
'filters' => 'true',
'detail' => $options['detail_display'] ?? 'sidebar',
'cluster' => $options['cluster_enabled'] ?? true,
'sidebar_position' => $options['sidebar_position'] ?? 'right',
'legend' => isset($options['show_legend']) ? ($options['show_legend'] ? 'true' : 'false') : 'false',
'show_detail_btn' => isset($options['show_detail_button']) ? ($options['show_detail_button'] ? 'true' : 'false') : 'true',
'detail_btn_text' => $options['detail_button_text'] ?? __('Ver detalle', 'fp-geo-content'),
'class' => '',
], $atts, 'fp-geo-map');
// Parsear valores
$post_types = array_filter(array_map('trim', explode(',', $atts['post_types'])));
$taxonomies = array_filter(array_map('trim', explode(',', $atts['taxonomies'])));
$show_filters = filter_var($atts['filters'], FILTER_VALIDATE_BOOLEAN);
$use_cluster = filter_var($atts['cluster'], FILTER_VALIDATE_BOOLEAN);
$show_legend = filter_var($atts['legend'], FILTER_VALIDATE_BOOLEAN);
$show_detail_btn = filter_var($atts['show_detail_btn'], FILTER_VALIDATE_BOOLEAN);
$sidebar_position = $atts['sidebar_position'];
if (empty($post_types)) {
return '<p class="fp-geo-error">' . __('No se han configurado tipos de contenido para el mapa.', 'fp-geo-content') . '</p>';
}
// Incrementar contador de instancias
self::$instance_count++;
$map_id = 'fp-geo-map-' . self::$instance_count;
// Cargar assets
wp_enqueue_style('fp-geo-content');
wp_enqueue_script('fp-geo-content');
// Obtener marcadores
$markers = FP_Geo_Data_Provider::get_markers([
'post_types' => $post_types,
]);
// Obtener términos para filtros
$filter_terms = [];
if ($show_filters && !empty($taxonomies)) {
$filter_terms = FP_Geo_Data_Provider::get_filter_terms($post_types, $taxonomies);
}
// Obtener configuración de tiles
$tile_provider = $options['tile_provider'] ?? 'carto_light';
$tile_providers = FP_Geo_Settings::get_tile_providers();
$tile_config = $tile_providers[$tile_provider] ?? $tile_providers['carto_light'];
// Obtener icono personalizado si existe
$marker_icon_id = $options['marker_icon'] ?? 0;
$marker_icon_url = $marker_icon_id ? wp_get_attachment_image_url($marker_icon_id, 'full') : '';
// Obtener etiquetas de los post types
$post_type_labels = [];
foreach ($post_types as $pt) {
$pt_obj = get_post_type_object($pt);
if ($pt_obj) {
$post_type_labels[$pt] = [
'singular' => $pt_obj->labels->singular_name,
'plural' => $pt_obj->labels->name,
];
}
}
// Obtener datos de leyenda si está habilitada
$legend_data = [];
$legend_taxonomy = $options['legend_taxonomy'] ?? '';
if ($show_legend && !empty($legend_taxonomy) && isset($filter_terms[$legend_taxonomy])) {
// Verificar si hay marcadores piloto (es_piloto está en los posts, no en los términos)
$has_pilot_markers = false;
foreach ($markers as $marker) {
if (isset($marker['es_piloto']) && $marker['es_piloto']) {
$has_pilot_markers = true;
break;
}
}
$legend_data = [
'taxonomy' => $legend_taxonomy,
'label' => $filter_terms[$legend_taxonomy]['label'],
'items' => $filter_terms[$legend_taxonomy]['terms'],
'has_pilots' => $has_pilot_markers,
];
}
// Configuración del mapa
$map_config = [
'mapId' => $map_id,
'center' => [(float) $atts['lat'], (float) $atts['lng']],
'zoom' => (int) $atts['zoom'],
'minZoom' => (int) ($options['min_zoom'] ?? 5),
'maxZoom' => (int) ($options['max_zoom'] ?? 18),
'markers' => $markers,
'filters' => $filter_terms,
'filterCombine' => $options['filter_combine'] ?? 'OR',
'detailDisplay' => $atts['detail'],
'clusterEnabled' => $use_cluster,
'tileUrl' => $tile_config['url'],
'tileAttribution' => $tile_config['attribution'],
'tileSubdomains' => $tile_config['subdomains'],
'defaultIcon' => FP_GEO_PLUGIN_URL . 'assets/img/marker-icon.png',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('fp_geo_nonce'),
'postTypes' => $post_types,
'postTypeLabels' => $post_type_labels,
// Nuevas opciones de marcadores
'markerIcon' => $marker_icon_url,
'markerDefaultColor' => $options['marker_default_color'] ?? '#F97316',
'useCategoryColors' => isset($options['use_category_colors']) && $options['use_category_colors'],
'legendTaxonomy' => $legend_taxonomy,
// Nuevas opciones de scroll
'scrollWheelZoom' => $options['scroll_wheel_zoom'] ?? 'ctrl',
// Nuevas opciones de display
'sidebarPosition' => $sidebar_position,
'showLegend' => $show_legend,
'legendData' => $legend_data,
'showDetailButton' => $show_detail_btn,
'detailButtonText' => $atts['detail_btn_text'],
'i18n' => [
'loading' => __('Cargando...', 'fp-geo-content'),
'noResults' => __('No se encontraron resultados', 'fp-geo-content'),
'viewMore' => $atts['detail_btn_text'],
'close' => __('Cerrar', 'fp-geo-content'),
'clearFilters' => __('Limpiar filtros', 'fp-geo-content'),
'scrollZoomHint' => __('Usa Ctrl + scroll para hacer zoom', 'fp-geo-content'),
],
];
// Pasar configuración al JS
wp_localize_script('fp-geo-content', 'fpGeoConfig_' . self::$instance_count, $map_config);
ob_start();
$wrapper_classes = [
'fp-geo-wrapper',
'fp-geo-detail-' . esc_attr($atts['detail']),
'fp-geo-sidebar-' . esc_attr($sidebar_position),
esc_attr($atts['class']),
];
?>
<div id="<?php echo esc_attr($map_id); ?>-wrapper"
class="<?php echo esc_attr(implode(' ', array_filter($wrapper_classes))); ?>"
data-instance="<?php echo esc_attr(self::$instance_count); ?>">
<?php if ($show_filters && !empty($filter_terms)): ?>
<?php echo self::render_filters($map_id, $filter_terms); ?>
<?php endif; ?>
<div class="fp-geo-map-container">
<div id="<?php echo esc_attr($map_id); ?>"
class="fp-geo-map"
style="height: <?php echo esc_attr($atts['height']); ?>;"></div>
<?php if ($show_legend && !empty($legend_data)): ?>
<?php echo self::render_legend($map_id, $legend_data); ?>
<?php endif; ?>
<?php echo self::render_detail_panel($map_id, $atts['detail'], $show_detail_btn, $atts['detail_btn_text']); ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Renderizar panel de filtros
*/
private static function render_filters($map_id, $filter_terms)
{
ob_start();
?>
<div class="fp-geo-filters" data-map="<?php echo esc_attr($map_id); ?>">
<div class="fp-geo-filters-inner">
<?php foreach ($filter_terms as $taxonomy => $data): ?>
<div class="fp-geo-filter-group" data-taxonomy="<?php echo esc_attr($taxonomy); ?>">
<label class="fp-geo-filter-label"><?php echo esc_html($data['label']); ?>:</label>
<div class="fp-geo-filter-buttons">
<?php foreach ($data['terms'] as $term): ?>
<button type="button"
class="fp-geo-filter-btn"
data-slug="<?php echo esc_attr($term['slug']); ?>"
data-taxonomy="<?php echo esc_attr($taxonomy); ?>"
style="<?php echo isset($term['color']) ? '--filter-color: ' . esc_attr($term['color']) . ';' : ''; ?>">
<?php if (isset($term['icono'])): ?>
<img src="<?php echo esc_url($term['icono']); ?>" alt="" class="fp-geo-filter-icon">
<?php endif; ?>
<span><?php echo esc_html($term['name']); ?></span>
</button>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<div class="fp-geo-filters-footer">
<span class="fp-geo-results-count">
<?php _e('Mostrando', 'fp-geo-content'); ?>
<strong class="fp-geo-results-number">0</strong>
<span class="fp-geo-results-label"><?php _e('resultados', 'fp-geo-content'); ?></span>
</span>
<button type="button" class="fp-geo-clear-filters">
<?php _e('Limpiar filtros', 'fp-geo-content'); ?>
</button>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Renderizar leyenda
*/
private static function render_legend($map_id, $legend_data)
{
if (empty($legend_data['items'])) {
return '';
}
$has_pilots = isset($legend_data['has_pilots']) && $legend_data['has_pilots'];
ob_start();
?>
<div id="<?php echo esc_attr($map_id); ?>-legend" class="fp-geo-legend">
<div class="fp-geo-legend-title"><?php _e('Leyenda', 'fp-geo-content'); ?></div>
<div class="fp-geo-legend-section">
<div class="fp-geo-legend-section-title"><?php _e('Iniciativas', 'fp-geo-content'); ?></div>
<div class="fp-geo-legend-items">
<?php foreach ($legend_data['items'] as $item):
$color = isset($item['color']) ? $item['color'] : '#F97316';
?>
<div class="fp-geo-legend-item">
<span class="fp-geo-legend-marker" style="background-color: <?php echo esc_attr($color); ?>;"></span>
<span class="fp-geo-legend-label"><?php echo esc_html($item['name']); ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<?php if ($has_pilots): ?>
<div class="fp-geo-legend-section fp-geo-legend-pilot-section">
<div class="fp-geo-legend-items">
<div class="fp-geo-legend-item fp-geo-legend-pilot">
<span class="fp-geo-legend-marker-pilot" aria-hidden="true">
<svg width="22" height="28" viewBox="0 0 36 48">
<circle cx="18" cy="18" r="15" fill="#888" stroke="white" stroke-width="3"/>
<polygon points="18,42 24,30 12,30" fill="#888" stroke="white" stroke-width="2" stroke-linejoin="round"/>
<text x="18" y="22" text-anchor="middle" fill="white" font-size="14" font-weight="bold">★</text>
</svg>
</span>
<span class="fp-geo-legend-label"><?php _e('Bloque Piloto', 'fp-geo-content'); ?></span>
</div>
<p class="fp-geo-legend-pilot-desc">
<?php _e('Actuación piloto (transversal a cualquier iniciativa)', 'fp-geo-content'); ?>
</p>
</div>
</div>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
/**
* Renderizar panel de detalle
*/
private static function render_detail_panel($map_id, $display_type, $show_button = true, $button_text = '')
{
$class = $display_type === 'modal' ? 'fp-geo-modal' : 'fp-geo-sidebar';
$button_text = $button_text ?: __('Ver más', 'fp-geo-content');
ob_start();
?>
<div id="<?php echo esc_attr($map_id); ?>-detail" class="fp-geo-detail <?php echo esc_attr($class); ?>">
<button class="fp-geo-detail-close" aria-label="<?php _e('Cerrar', 'fp-geo-content'); ?>">×</button>
<div class="fp-geo-detail-content">
<div class="fp-geo-detail-header">
<div class="fp-geo-detail-taxonomies"></div>
</div>
<div class="fp-geo-detail-thumbnail"></div>
<h2 class="fp-geo-detail-title"></h2>
<div class="fp-geo-detail-excerpt"></div>
<div class="fp-geo-detail-meta">
<div class="fp-geo-detail-location"></div>
<div class="fp-geo-detail-contact"></div>
</div>
<?php if ($show_button): ?>
<div class="fp-geo-detail-footer">
<a href="#" class="fp-geo-detail-link fp-geo-btn" target="_blank">
<?php echo esc_html($button_text); ?>
</a>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($display_type === 'modal'): ?>
<div id="<?php echo esc_attr($map_id); ?>-overlay" class="fp-geo-overlay"></div>
<?php endif; ?>
<?php
return ob_get_clean();
}
}