Geoposicionador de cosas

This commit is contained in:
Jose Ibáñez
2026-02-13 13:14:41 +01:00
commit a17bd50744
10 changed files with 3440 additions and 0 deletions
@@ -0,0 +1,357 @@
<?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();
}
}