Skip Navigation

[Assigned] Map Issues: Can’t filter a list of related posts and can’t generate hover text

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
- 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 10:00 – 13:00 -
- 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 14:00 – 18:00 -

Supporter timezone: Asia/Kolkata (GMT+05:30)

This topic contains 0 replies, has 1 voice.

Last updated by Djuna 5 hours, 35 minutes ago.

Assisted by: Minesh.

Author
Posts
#2806723
Map Filter + Hover Text Issues .jpg

For convenience, here's an image reviewing where I stand on my map issues.

Just now I'm freshly backed up and have the content template set to the Anthony Bourdain page, good for testing:

hidden link
hidden link

If you have a chance for a look today, that would be amazing!

#2806766
JS Focus Map.jpg

Now I'm trying with a "map focus" link on each location card.

This would be a preferable interface and if I can get it working I'll do away with the search for my Locaton View.

But sadly, I'm still getting sent to 0,0 coordinates with this approach.

The results are slightly different than when using the dropdown filter though, where I don't get any map tiles. Wtith the "map focus" approach, I get map tiles but they're all zoomed in on a patch of ocean west of Africa.

Here's the doc I'm following:

https://toolset.com/documentation/programmer-reference/maps/maps-shortcodes/#map-focus-on-marker

And here's the html snippet used to generate links in my view loop:

<a href="#" class="js-wpv-addon-maps-focus-map js-toolset-maps-hover-map-3-marker-mark js-toolset-maps-open-infowindow-map-3-marker-mark" data-map="Project-Locations" data-marker="[wpv-post-id]-marker-id">[types field='location-name'][/types]</a>
#2807067

I've just removed the search/filter fields for this map as they were a formatting headache for mobile view and I hope to get the "map focus on marker" approach working.

Fingers crossed Minesh can have a look today 🙂

I just made a fresh backup...

#2807295

Christopher Amirian
Supporter

Languages: English (English )

Hi,

Minesh is not available today. He will check the map tomorrow. Thanks.

#2807578

Minesh
Supporter

Languages: English (English )

Timezone: Asia/Kolkata (GMT+05:30)

Ok - I would like to know where are you at now and where exactly you reuiqre help.

I see the ticket was created for different issue and now you are asking to get help for different issue.

*** Please make a FULL BACKUP of your database and website.***
I would also eventually need to request temporary access (WP-Admin) to your site. Preferably to a test site where the problem has been replicated if possible in order to be of better help and check if some configurations might need to be changed.

I have set the next reply to private which means only you and I have access to it.

#2807803

Revisiting my custom map where I overcame the 0,0 coordinate problem for AJAX updates on this hidden link">archive, my AI-assisted javascript lives in functions.php, not the Custom JS window for the template.

While I hope there's a simple solution for my map troubles on project posts (does my link snippet look okay?), if not, maybe there are useful elements here for more broadly addressing the 0,0 coordinate problem that I and some others have faced.

See, in particular, " // Set up DOM change monitoring " and below.

Again, I'm not looking for support on this, just sharing...

function indonesia_locations_map($atts) {
    $atts = shortcode_atts(array(
        'limit' => '100',
        'zoom' => '5',
        'height' => '500px',
        'width' => '100%',
        'decimals' => '4',
        'center_lat' => '-2.5', // Default center on Indonesia
        'center_lng' => '118',
        'search_form_id' => '25770', // Your specific search form ID
        'debug' => 'false', // Set to 'true' to enable verbose debugging
        'cache' => 'true',   // Set to 'false' to disable map caching
        'max_zoom' => '12'   // Maximum zoom level (lower = less zoom)
    ), $atts);
    
    // Enqueue Google Maps API
    wp_enqueue_script('google-maps', '<em><u>hidden link</u></em>', array('jquery'), null, true);
    
    ob_start();
    
    try {
        // Get locations
        $locations = get_posts(array(
            'post_type' => 'location',
            'numberposts' => intval($atts['limit']),
            'orderby' => 'title',
            'order' => 'ASC',
            'meta_query' => array(
                array(
                    'key' => 'wpcf-coordinates',
                    'compare' => 'EXISTS'
                )
            )
        ));
        
        if (empty($locations)) {
            return '<p>No locations with coordinates found.</p>';
        }
        
        // Generate a unique ID for this map
        $map_id = 'indonesia_map_' . rand(10000, 99999);
        
        // Start the map output
        echo '<div class="indonesia-map-container" style="width:' . esc_attr($atts['width']) . ';">';
        echo '<div id="' . esc_attr($map_id) . '" class="google-map" style="height:' . esc_attr($atts['height']) . ';"></div>';
        
        // Add a refresh button
        echo '<button id="refresh-map-' . esc_attr($map_id) . '" class="refresh-map-button">Refresh Map</button>';
        
        // Add a debug panel
        echo '<div id="debug-panel-' . esc_attr($map_id) . '" class="debug-panel" style="margin-top: 10px; padding: 10px; border: 1px solid #ddd; background: #f9f9f9; max-height: 200px; overflow-y: auto; display: none;">
            <h4 style="margin-top: 0;">Debug Info</h4>
            <div id="debug-content-' . esc_attr($map_id) . '"></div>
        </div>';
        
        // Track which locations we've already processed to avoid duplicates
        $processed_locations = array();
        $valid_locations = 0;
        $location_data = array();
        $all_projects = array();
    
    
// Process locations
foreach ($locations as $location) {
    $location_id = $location->ID;
    
    // Skip if we've already processed this location
    if (in_array($location_id, $processed_locations)) {
        continue;
    }
    
    $processed_locations[] = $location_id;
    $coordinates = get_post_meta($location_id, 'wpcf-coordinates', true);
    
    if ($coordinates) {
        // Clean up coordinates - handle both comma format and curly brace format
        $coordinates = str_replace(array('{', '}'), '', $coordinates);
        
        // Split into lat and lng
        $coords_array = explode(',', $coordinates);
        if (count($coords_array) == 2) {
            $lat = trim($coords_array[0]);
            $lng = trim($coords_array[1]);
            
            // Skip invalid coordinates (0,0 or empty)
            if (empty($lat) || empty($lng) || 
                (abs(floatval($lat)) < 0.0001 && abs(floatval($lng)) < 0.0001)) {
                continue;
            }
            
            // Skip coordinates that are clearly outside Indonesia region
            // Indonesia is roughly between -11 to 6 latitude and 95 to 141 longitude
            $lat_float = floatval($lat);
            $lng_float = floatval($lng);
            if ($lat_float < -11 || $lat_float > 6 || $lng_float < 95 || $lng_float > 141) {
                continue;
            }
            
            $valid_locations++;
            
            // Truncate to specified decimal places
            $lat = number_format($lat_float, intval($atts['decimals']), '.', '');
            $lng = number_format($lng_float, intval($atts['decimals']), '.', '');
            
            // Get projects associated with this location
            $related_projects = array();
            $project_info = array();
            
            if (function_exists('toolset_get_related_posts')) {
                $related_projects = toolset_get_related_posts(
                    $location_id,
                    'project-location',
                    [
                        'query_by_role' => 'child',
                        'role_to_return' => 'parent',
                        'limit' => 100,
                        'return' => 'post_object',
                        'orderby' => 'title',
                        'order' => 'ASC'
                    ]
                );
                
                foreach ($related_projects as $project) {
                    $project_id = $project->ID;
                    
                    // Get project taxonomies with both name and slug
                    $islands = array();
                    $regions = array();
                    $genres = array();
                    $kinds = array();
                    $content = array();
                    
                    // Get island terms with both name and slug
                    $island_terms = wp_get_post_terms($project_id, 'island');
                    foreach ($island_terms as $term) {
                        $islands[] = array('name' => $term->name, 'slug' => $term->slug);
                    }
                    
                    // Get region terms with both name and slug
                    $region_terms = wp_get_post_terms($project_id, 'region');
                    foreach ($region_terms as $term) {
                        $regions[] = array('name' => $term->name, 'slug' => $term->slug);
                    }
                    
                    // Get genre terms with both name and slug
                    $genre_terms = wp_get_post_terms($project_id, 'genre');
                    foreach ($genre_terms as $term) {
                        $genres[] = array('name' => $term->name, 'slug' => $term->slug);
                    }
                    
                    // Get kind terms with both name and slug
                    $kind_terms = wp_get_post_terms($project_id, 'kind');
                    foreach ($kind_terms as $term) {
                        $kinds[] = array('name' => $term->name, 'slug' => $term->slug);
                    }
                    
                    // Get content terms with both name and slug
                    $content_terms = wp_get_post_terms($project_id, 'featured-content');
                    foreach ($content_terms as $term) {
                        $content[] = array('name' => $term->name, 'slug' => $term->slug);
                    }
                    
                    $project_info[] = array(
                        'id' => $project_id,
                        'title' => $project->post_title,
                        'url' => get_permalink($project_id),
                        'islands' => $islands,
                        'regions' => $regions,
                        'genres' => $genres,
                        'kinds' => $kinds,
                        'content' => $content
                    );
                    
                    // Track all projects for search functionality
                    if (!isset($all_projects[$project_id])) {
                        $all_projects[$project_id] = array(
                            'id' => $project_id,
                            'title' => $project->post_title,
                            'url' => get_permalink($project_id),
                            'islands' => $islands,
                            'regions' => $regions,
                            'genres' => $genres,
                            'kinds' => $kinds,
                            'content' => $content,
                            'locations' => array()
                        );
                    }
                    
                    $all_projects[$project_id]['locations'][] = array(
                        'id' => $location_id,
                        'title' => $location->post_title,
                        'lat' => $lat,
                        'lng' => $lng
                    );
                }
            }
            
            // Initialize region variables
            $region = '';
            $region_debug = array();
            
            // Try common Toolset field name patterns
            $possible_field_names = [
                'wpcf-region',
                'region',
                'location-region',
                'wpcf_region',
                '_wpcf_belongs_region_id'
            ];

            foreach ($possible_field_names as $field_name) {
                $value = get_post_meta($location_id, $field_name, true);
                $region_debug["field_{$field_name}"] = !empty($value) ? $value : 'EMPTY';

                if (!empty($value)) {
                    $region = $value;
                    // Use error_log instead of echo for debugging in PHP
                    error_log("Found region using field name '{$field_name}': " . $value);
                }
            }
            
            // Try using Types API
            if (function_exists('types_render_field')) {
                $types_region = types_render_field('region', array('post_id' => $location_id));
                
                // Use error_log instead of echo for debugging
                error_log("Location #{$location_id} ({$location->post_title}) - Region value: " . 
                     (empty($types_region) ? "EMPTY" : $types_region));
                
                $region_debug['types_field_exists'] = !empty($types_region) ? 'Yes' : 'No';
                
                if (empty($region) && !empty($types_region)) {
                    $region = $types_region;
                    $region_debug['region_from_types'] = $region;
                }
            }
            
            // Store location data for JavaScript
            $location_data[] = array(
                'id' => $location_id,
                'title' => $location->post_title,
                'lat' => $lat,
                'lng' => $lng,
                'region' => $region,
                'region_debug' => $region_debug,
                'projects' => $project_info
            );
        }
    }
}
        
        echo '</div>'; // Close indonesia-map-container
        
        // Add a count of locations shown
        echo '<p class="location-count"><small>Showing <span id="visible-locations-count-' . esc_attr($map_id) . '">' . $valid_locations . '</span> locations.</small></p>';
        
        // Add custom styles
        echo '<style>
            .indonesia-map-container {
                position: relative;
                margin-bottom: 20px;
            }
            .google-map {
                width: 100%;
            }
            .gm-style-mtc button {
                color: #888888 !important;
                background-color: rgba(255, 255, 255, 0.5) !important;
            }
            .gm-svpc, .gm-fullscreen-control, .gm-control-active {
                background-color: rgba(255, 255, 255, 0.5) !important;
            }
            .gm-style-iw {
                padding: 10px 0 5px 0;
            }
            .map-info-window {
				padding: 0;
				max-width: 300px;
				padding-bottom: 5px;
			}

			/* Title of the location */
			.map-info-window h4 {
				margin-top: 0;
				margin-bottom: 3px;
				color: #380501;
				font-size: 16px;
				font-face: "Alegreya Sans SC"
			}

            /* "Region" */
            .map-info-window .location-region {
				margin: 5px 0;
				color: #380501;
				font-size: 16px;
				font-face: "Alegreya Sans SC"
                text-transform: uppercase;
			}

			/* "Featured in:" text */
			.map-info-window .featured-in {
				margin-bottom: 5px;
				font-weight: bold;
			}

			/* Project list container */
			.map-info-window .project-list {
				margin: 0;
				padding-left: 15px;
			}

			/* Individual project items */
			.map-info-window .project-item {
				margin-bottom: 3px;
                color: #bd5900;
			}

			/* Project links */
			.map-info-window .project-link {
				color: #bd5900;
				text-decoration: none;
			}

			.map-info-window .project-link:hover {
				color: #c97600
			}

			/* Custom close button */
			.custom-close-button {
				position: absolute;
				top: 3px;
				right: 3px;
				width: 16px;
				height: 16px;
				font-size: 12px;
				line-height: 16px;
				text-align: center;
				background: #f5f5f5;
				border-radius: 50%;
				color: #666;
				cursor: pointer;
				border: 1px solid #ddd;
			}

			.custom-close-button:hover {
				background: #eee;
				color: #333;
			}

			/* Google info window styling overrides */
			.gm-style-iw {
				padding: 10px 0 5px 0;
			}

			/* Hide the default close button */
			.gm-ui-hover-effect {
				display: none !important;
			}

            /* Add a smaller, custom close button 
           // .custom-close-button {
               // position: absolute;
               // top: 3px;
               // right: 3px;
               // width: 16px;
               // height: 16px;
               // font-size: 12px;
               // line-height: 16px;
               // text-align: center;
               // background: #f5f5f5;
               // border-radius: 50%;
               // color: #666;
               // cursor: pointer;
               // border: 1px solid #ddd;
          //  }
          //  .custom-close-button:hover {
             //   background: #eee;
             //   color: #333;
         //   }  */
            /* Loading spinner */
            .map-loading {
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: rgba(255,255,255,0.8);
                padding: 15px 20px;
                border-radius: 5px;
                box-shadow: 0 2px 5px rgba(0,0,0,0.2);
                z-index: 1000;
            }
            .map-loading:after {
                content: "";
                display: inline-block;
                width: 20px;
                height: 20px;
                margin-left: 10px;
                vertical-align: middle;
                border: 2px solid #ccc;
                border-radius: 50%;
                border-top: 2px solid #333;
                animation: spin 1s linear infinite;
            }
            @keyframes spin {
                0% { transform: rotate(0deg); }
                100% { transform: rotate(360deg); }
            }
            /* Hide Google\'s reset map button (north arrow) */
            .gm-control-active.gm-compass-turn {
                display: none !important;
            }
        </style>';
        
        // Add JavaScript for map functionality
        echo '<script>
        jQuery(document).ready(function($) {
            // Store location and project data
            var locationData = ' . json_encode($location_data) . ';
            var allProjects = ' . json_encode($all_projects) . ';
            var mapId = "' . esc_js($map_id) . '";
            var searchFormId = "' . esc_js($atts['search_form_id']) . '";
            var map, infoWindow, markers = [];
            var currentMarker = null;
            var lastCriteria = null;
            var syncInterval = null;
            var mapInitialized = false;
            var mapFullyLoaded = false;
            var domChangeCounter = 0;
            var maxDomChangesBeforeReset = 10; // Adjust this threshold as needed
            var formObserver = null;
            var initialFilterApplied = false;
            var filterAttempts = 0;
            var maxFilterAttempts = 5;
            var debugMode = ' . ($atts['debug'] === 'true' ? 'true' : 'false') . ';
            var useCache = ' . ($atts['cache'] === 'true' ? 'true' : 'false') . ';
            var mapCacheKey = "indonesia_map_cache_" + window.location.pathname;
            var mapCacheExpiry = 24 * 60 * 60 * 1000; // 24 hours
            var filterDebounceTimer = null;
            var resetCooldown = false;
            var maxZoomLevel = ' . esc_js($atts['max_zoom']) . '; // Maximum zoom level
            
            // Add loading indicator to map
            $("#" + mapId).append("<div id=\'loading-" + mapId + "\' class=\'map-loading\'>Loading map</div>");
            
            // Debug function
            function debugLog(message, data, force) {
                if (debugMode || force) {
                    console.log(message, data);
                    var content = "<div><strong>" + message + "</strong>";
                    if (data) {
                        content += ": " + JSON.stringify(data);
                    }
                    content += "</div>";
                    $("#debug-content-" + mapId).prepend(content);
                }
            }
            
            // Show debug panel with keyboard shortcut (Ctrl+Shift+D)
            $(document).keydown(function(e) {
                if (e.ctrlKey && e.shiftKey && e.keyCode === 68) {
                    $("#debug-panel-" + mapId).toggle();
                    return false;
                }
            });
            
            debugLog("Map setup starting with " + locationData.length + " locations", null, true);
            
            // Cache handling functions
            function saveMapState(criteria, visibleMarkers) {
                if (!useCache) return;
                
                try {
                    var cacheData = {
                        criteria: criteria,
                        visibleMarkers: visibleMarkers,
                        timestamp: Date.now()
                    };
                    localStorage.setItem(mapCacheKey, JSON.stringify(cacheData));
                    debugLog("Map state saved to cache", criteria);
                } catch (e) {
                    debugLog("Error saving to cache", e.message, true);
                }
            }
            
            function loadMapState() {
                if (!useCache) return null;
                
                try {
                    var cacheData = localStorage.getItem(mapCacheKey);
                    if (!cacheData) return null;
                    
                    var parsedData = JSON.parse(cacheData);
                    
                    // Check if cache is expired
                    if (Date.now() - parsedData.timestamp > mapCacheExpiry) {
                        localStorage.removeItem(mapCacheKey);
                        return null;
                    }
                    
                    debugLog("Loaded map state from cache", parsedData.criteria);
                    return parsedData;
                } catch (e) {
                    debugLog("Error loading from cache", e.message, true);
                    return null;
                }
            }
            
            // Helper function to check for active filters
            function checkForActiveFilters(criteria) {
                if (criteria.search && criteria.search.trim().length > 0) return true;
                if (criteria.island && criteria.island !== "0" && criteria.island !== "") return true;
                if (criteria.region && criteria.region !== "0" && criteria.region !== "") return true;
                if (criteria.genre && criteria.genre !== "0" && criteria.genre !== "") return true;
                if (criteria.kind && criteria.kind !== "0" && criteria.kind !== "") return true;
                if (criteria.content && criteria.content !== "0" && criteria.content !== "") return true;
                return false;
            }
            
            // Helper function to check if a term matches criteria
            function termMatches(term, criteria) {
                if (!term || !criteria) return false;
                
                // Check if term is an object with name and slug
                if (typeof term === "object" && term.name && term.slug) {
                    return term.name.toLowerCase() === criteria.toLowerCase() || 
                           term.slug.toLowerCase() === criteria.toLowerCase();
                }
                
                // Fall back to direct string comparison
                return term.toLowerCase() === criteria.toLowerCase();
            }
            
            // Debounced filter function to prevent multiple rapid filter operations
            function debouncedFilterMap(criteria) {
                // Clear any existing timeout
                if (filterDebounceTimer) {
                    clearTimeout(filterDebounceTimer);
                }
                
                // Set a new timeout
                filterDebounceTimer = setTimeout(function() {
                    filterMap(criteria);
                }, 250); // 250ms debounce
            }
            
            // Initialize the map
            function initMap() {
                if (mapInitialized) {
                    debugLog("Map already initialized, skipping");
                    return;
                }
                
                debugLog("Initializing map", null, true);
                
                // Custom map styles
                var customMapStyles = [
                    {
                        "featureType": "all",
                        "elementType": "labels",
                        "stylers": [
                            { "visibility": "on" }
                        ]
                    }
                ];
                
                // Create the map
                map = new google.maps.Map(document.getElementById(mapId), {
                    center: {
                        lat: ' . esc_js($atts['center_lat']) . ', 
                        lng: ' . esc_js($atts['center_lng']) . '
                    },
                    zoom: ' . esc_js($atts['zoom']) . ',
                    mapTypeId: google.maps.MapTypeId.HYBRID, // Set default to HYBRID view
                    mapTypeControl: true,
                    streetViewControl: true,
                    fullscreenControl: true,
                    styles: customMapStyles,
                    maxZoom: parseInt(maxZoomLevel), // Set maximum zoom level
                    // Style the map controls
                    mapTypeControlOptions: {
                        style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
                        position: google.maps.ControlPosition.TOP_RIGHT
                    },
                    zoomControlOptions: {
                        position: google.maps.ControlPosition.RIGHT_CENTER
                    },
                    streetViewControlOptions: {
                        position: google.maps.ControlPosition.RIGHT_CENTER
                    },
                    fullscreenControlOptions: {
                        position: google.maps.ControlPosition.RIGHT_TOP
                    }
                });

                // Apply custom styles to map controls after the map is loaded
                google.maps.event.addListenerOnce(map, "idle", function() {
                    // Apply custom styles to map controls
                    var mapControls = document.querySelectorAll(".gm-style-mtc button, .gm-svpc, .gm-fullscreen-control");
                    mapControls.forEach(function(control) {
                        control.style.color = "#000";
                        control.style.backgroundColor = "rgba(255, 255, 255, 0.5)";
                    });
                    
                    // Hide Google reset map button (north arrow)
                    var resetButtons = document.querySelectorAll(".gm-control-active.gm-compass-turn");
                    resetButtons.forEach(function(button) {
                      //  button.style.display = "none";
                    });
                });
                
                // Create a single info window to reuse
                infoWindow = new google.maps.InfoWindow({
                    pixelOffset: new google.maps.Size(0, -5)
                });
                

				// Custom SVG marker with your exact color
					var pinColor = "bd5900";  // Orange hex color without #
					var customMarkerIcon = {
					path: "M 12,2 C 8.1340068,2 5,5.1340068 5,9 c 0,5.25 7,13 7,13 0,0 7,-7.75 7,-13 0,-3.8659932 -3.134007,-7 -7,-7 z",
					fillColor: "#" + pinColor,
					fillOpacity: 1,
					strokeColor: "#FFFFFF",
					strokeWeight: 1,
					scale: 1.1,
					anchor: new google.maps.Point(12, 22)
				};
                
                // Add markers for each location but keep them hidden initially
                var bounds = new google.maps.LatLngBounds();
                
                $.each(locationData, function(i, location) {
                    var position = new google.maps.LatLng(
                        parseFloat(location.lat),
                        parseFloat(location.lng)
                    );
                    
                    var marker = new google.maps.Marker({
                        position: position,
                        map: map,
                        title: location.title,
                        locationId: location.id,
                        projects: location.projects,
                        visible: false, // Start with hidden markers
                        icon: customMarkerIcon // Apply custom marker style
                    });
                    
                    // Add click listener to show info window
                    marker.addListener("click", function() {
                        showInfoWindow(marker);
                        currentMarker = marker;
                    });
                    
                    // Extend bounds to include this marker
                    bounds.extend(position);
                    
                    // Store the marker
                    markers.push(marker);
                });
                
                // Fit map to bounds if we have locations
                if (markers.length > 0) {
                    map.fitBounds(bounds);
                    
                    // Add a listener for when bounds_changed event is fired
                    google.maps.event.addListenerOnce(map, "bounds_changed", function() {
                        if (map.getZoom() > maxZoomLevel) {
                            map.setZoom(parseInt(maxZoomLevel));
                        }
                    });
                }
                
                // Add reset button to map
                var resetButton = document.createElement("div");
                resetButton.className = "map-reset-button";
                resetButton.innerHTML = "Reset Map";
                resetButton.onclick = resetMap;
                
                map.controls[google.maps.ControlPosition.BOTTOM_RIGHT].push(resetButton);
                
                // Close info window when clicking elsewhere on the map
                google.maps.event.addListener(map, "click", function() {
                    infoWindow.close();
                    currentMarker = null;
                });
                
                // Add event listener for when the info window is closed
                google.maps.event.addListener(infoWindow, "closeclick", function() {
                    currentMarker = null;
                });
                
                debugLog("Map initialized with " + markers.length + " markers", null, true);
                mapInitialized = true;
                
                // Connect to Toolset Search
                connectToToolsetSearch();
                
                // Set up DOM change monitoring
                monitorFormDomChanges();
                
                // Set up periodic sync - increased from 3s to 5s to reduce frequency
                syncInterval = setInterval(syncWithForm, 5000);
                
                // Wait for map to be fully loaded before checking filters
                google.maps.event.addListenerOnce(map, "idle", function() {
                    mapFullyLoaded = true;
                    debugLog("Map fully loaded and idle, checking for filters", null, true);
                    
                    // Try to load from cache first
                    var cachedState = loadMapState();
                    if (cachedState && cachedState.criteria) {
                        debugLog("Applying cached filter state", cachedState.criteria, true);
                        initialFilterApplied = true;
                        filterMap(cachedState.criteria, cachedState.visibleMarkers);
                    } else {
                        // Check for filters from the form
                        setTimeout(checkExistingFilters, 500);
                    }
                    
                    // Remove loading indicator
                    $("#loading-" + mapId).remove();
                });
                
                // Check for any active filters after a delay
                setTimeout(function() {
                    if (mapInitialized && mapFullyLoaded && !initialFilterApplied) {
                        var searchForm = $("form[name=\"wpv-filter-" + searchFormId + "\"]");
                        if (searchForm.length) {
                            var criteria = getSearchCriteriaFromForm(searchForm);
                            var hasActiveFilters = checkForActiveFilters(criteria);
                            
                            if (hasActiveFilters) {
                                debugLog("Found active filters on delayed check", criteria, true);
                                filterMap(criteria);
                            } else {
                                // If no filters, show all markers
                                showAllMarkers();
                            }
                        } else {
                            // If no form found, show all markers
                            showAllMarkers();
                        }
                    }
                }, 2000);
            }
            
            // Function to show all markers
            function showAllMarkers() {
                if (!mapInitialized) return;
                
                $.each(markers, function(i, marker) {
                    marker.setVisible(true);
                });
                
                // Update the visible count
                $("#visible-locations-count-" + mapId).text(markers.length);
                
                // Remove loading indicator if it exists
                $("#loading-" + mapId).remove();
            }
            
            // Function to monitor DOM changes in the form
            function monitorFormDomChanges() {
                var searchForm = $("form[name=\"wpv-filter-" + searchFormId + "\"]");
                
                if (searchForm.length) {
                    // Clean up previous observer if it exists
                    if (formObserver) {
                        formObserver.disconnect();
                    }
                    
                    // Use MutationObserver to detect DOM changes
                    formObserver = new MutationObserver(function(mutations) {
                        domChangeCounter++;
                        debugLog("Form DOM changed (count: " + domChangeCounter + ")");
                        
                        // If we reached the threshold, reset the connection
                        if (domChangeCounter >= maxDomChangesBeforeReset) {
                            debugLog("DOM change threshold reached, resetting connection", null, true);
                            domChangeCounter = 0;
                            
                            // Disconnect and reconnect to Toolset Search
                            formObserver.disconnect();
                            setTimeout(function() {
                                connectToToolsetSearch();
                                monitorFormDomChanges();
                            }, 100);
                        }
                    });
                    
                    // Configure the observer to watch for changes to the form structure
                    formObserver.observe(searchForm[0], { 
                        childList: true, 
                        subtree: true,
                        attributes: true
                    });
                    
                    return formObserver;
                }
                
                return null;
            }
            
            // Reinitialize map after AJAX updates
            function reinitializeMapIfNeeded() {
                var mapElement = document.getElementById(mapId);
                if (!mapElement) {
                    debugLog("Map element not found, map may have been removed from DOM", null, true);
                    return;
                }
                
                if (!mapInitialized || !map) {
                    debugLog("Map needs to be reinitialized", null, true);
                    initMap();
                } else {
                    debugLog("Map already exists, no need to reinitialize");
                }
            }
            
            // Sync map with current form values
            function syncWithForm() {
                if (!mapInitialized || !mapFullyLoaded) {
                    debugLog("Map not fully initialized yet, skipping sync");
                    return;
                }
                
                reinitializeMapIfNeeded();
                
                var searchForm = $("form[name=\"wpv-filter-" + searchFormId + "\"]");
                
                if (searchForm.length) {
                    var criteria = getSearchCriteriaFromForm(searchForm);
                    var criteriaStr = JSON.stringify(criteria);
                    
                    if (criteriaStr !== lastCriteria) {
                        debugLog("Sync detected form change", criteria);
                        debouncedFilterMap(criteria);
                    }
                }
            }
            
            // Get search criteria from a form
            function getSearchCriteriaFromForm(form) {
                return {
                    search: form.find("input[name=\"wpv_post_search\"]").val() || "",
                    island: form.find("select[name=\"wpv-island\"]").val() || "0",
                    region: form.find("select[name=\"wpv-region\"]").val() || "0",
                    genre: form.find("select[name=\"wpv-genre\"]").val() || "0",
                    kind: form.find("select[name=\"wpv-kind\"]").val() || "0",
                    content: form.find("select[name=\"wpv-featured-content\"]").val() || "0"
                };
            }
            
            // Check for existing filter values
            function checkExistingFilters() {
                if (!mapInitialized || !mapFullyLoaded) {
                    debugLog("Map not fully initialized yet, will check filters later");
                    setTimeout(checkExistingFilters, 1000);
                    return;
                }
                
                if (initialFilterApplied) {
                    debugLog("Initial filter already applied, skipping");
                    return;
                }
                
                var searchForm = $("form[name=\"wpv-filter-" + searchFormId + "\"]");
                
                if (searchForm.length) {
                    var criteria = getSearchCriteriaFromForm(searchForm);
                    var hasFilters = checkForActiveFilters(criteria);
                    
                    if (hasFilters) {
                        debugLog("Found existing filters on page load", criteria, true);
                        initialFilterApplied = true;
                        
                        // Force a small delay to ensure the map is ready
                        setTimeout(function() {
                            filterMap(criteria);
                        }, 500);
                    } else {
                        debugLog("No filters found on page load");
                        // Make sure all markers are visible
                        showAllMarkers();
                    }
                } else {
                    debugLog("Search form not found, will try again");
                    setTimeout(checkExistingFilters, 1000);
                }
            }
            
            // Function to show info window for a marker
            function showInfoWindow(marker) {
                var content = "<div class=\"map-info-window\">";
                content += "<span class=\"custom-close-button\">×</span>";
                content += "<h4>" + marker.title + "</h4>";

               // Display the region if available
    				if (marker.region && marker.region.length > 0) {
        			content += "<p class=\"location-region\"><strong>Region:</strong> " + marker.region + "</p>";
    				}
                
                if (marker.projects && marker.projects.length > 0) {
                    content += "<p class=\"featured-in\">Featured in:</p>";
                    content += "<ul class=\"project-list\">";
                    
                    $.each(marker.projects, function(i, project) {
                        content += "<li class=\"project-item\" data-project-id=\"" + project.id + "\">";
                        content += "<a href=\"" + project.url + "\" class=\"project-link\">" + project.title + "</a>";
                        content += "</li>";
                    });
                    
                    content += "</ul>";
                } else {
                    content += "<p class=\"no-projects\">No associated projects.</p>";
                }
                
                content += "</div>";
                
                infoWindow.setContent(content);
                infoWindow.open(map, marker);
                
                // Add click handler for custom close button after info window is opened
                google.maps.event.addListener(infoWindow, "domready", function() {
                    $(".custom-close-button").on("click", function() {
                        infoWindow.close();
                        currentMarker = null;
                    });
                });
            }
            
            // Function to filter map by search criteria
            function filterMap(criteria, precomputedMarkers) {
                if (!mapInitialized || !map) {
                    debugLog("Map not initialized, cannot filter", null, true);
                    return false;
                }
                
                debugLog("Filtering map with criteria", criteria, true);
                
                // Store the criteria for comparison
                lastCriteria = JSON.stringify(criteria);
                
                // Check if we have any active criteria
                var hasActiveCriteria = checkForActiveFilters(criteria);
                
                // If no active criteria, reset the map
                if (!hasActiveCriteria) {
                    debugLog("No active criteria, resetting map", null, true);
                    resetMap();
                    return true;
                }
                
                // If we have precomputed markers from cache, use them
                if (precomputedMarkers) {
                    debugLog("Using precomputed markers from cache", null, true);
                    var visibleCount = 0;
                    var bounds = new google.maps.LatLngBounds();
                    var boundsExtended = false;
                    
                    $.each(markers, function(i, marker) {
                        var shouldShow = precomputedMarkers.indexOf(marker.locationId) !== -1;
                        marker.setVisible(shouldShow);
                        
                        if (shouldShow) {
                            visibleCount++;
                            bounds.extend(marker.getPosition());
                            boundsExtended = true;
                        }
                    });
                    
                    // Update the visible count
                    $("#visible-locations-count-" + mapId).text(visibleCount);
                    
                    // Fit map to bounds if we have visible markers
                    if (boundsExtended) {
                        map.fitBounds(bounds);
                        
                        // Add a listener for when bounds_changed event is fired
                        google.maps.event.addListenerOnce(map, "bounds_changed", function() {
                            if (map.getZoom() > maxZoomLevel) {
                                map.setZoom(parseInt(maxZoomLevel));
                            }
                        });
                        
                        debugLog("Fitting map to bounds with " + visibleCount + " visible markers", null, true);
                    }
                    
                    // Remove loading indicator if it exists
                    $("#loading-" + mapId).remove();
                    
                    return visibleCount > 0;
                }
                
                // Find matching projects first
                var matchingProjects = [];
                
                $.each(allProjects, function(projectId, project) {
                    var projectMatches = true;
                    
                    // Check text search
                    if (criteria.search && criteria.search.trim().length > 0) {
                        if (project.title.toLowerCase().indexOf(criteria.search.toLowerCase()) === -1) {
                            projectMatches = false;
                            return true; // Continue to next project
                        }
                    }
                    
                    // Check island
                    if (criteria.island && criteria.island !== "0" && criteria.island !== "") {
                        var islandMatch = false;
                        
                        if (debugMode) {
                            debugLog("Checking project " + project.title + " for island: " + criteria.island);
                            debugLog("Project islands: ", project.islands);
                        }
                        
                        $.each(project.islands, function(i, island) {
                            if (termMatches(island, criteria.island)) {
                                islandMatch = true;
                                if (debugMode) debugLog("Island match found: ", island);
                                return false; // Break the loop
                            }
                        });
                        if (!islandMatch) {
                            projectMatches = false;
                            if (debugMode) debugLog("No island match for project: " + project.title);
                            return true; // Continue to next project
                        }
                    }
                    
                    // Check region
                    if (criteria.region && criteria.region !== "0" && criteria.region !== "") {
                        var regionMatch = false;
                        
                        if (debugMode) {
                            debugLog("Checking project " + project.title + " for region: " + criteria.region);
                            debugLog("Project regions: ", project.regions);
                        }
                        
                        $.each(project.regions, function(i, region) {
                            if (termMatches(region, criteria.region)) {
                                regionMatch = true;
                                if (debugMode) debugLog("Region match found: ", region);
                                return false; // Break the loop
                            }
                        });
                        if (!regionMatch) {
                            projectMatches = false;
                            if (debugMode) debugLog("No region match for project: " + project.title);
                            return true; // Continue to next project
                        }
                    }
                    
                    // Check genre
                    if (criteria.genre && criteria.genre !== "0" && criteria.genre !== "") {
                        var genreMatch = false;
                        
                        if (debugMode) {
                            debugLog("Checking project " + project.title + " for genre: " + criteria.genre);
                            debugLog("Project genres: ", project.genres);
                        }
                        
                        $.each(project.genres, function(i, genre) {
                            if (termMatches(genre, criteria.genre)) {
                                genreMatch = true;
                                if (debugMode) debugLog("Genre match found: ", genre);
                                return false; // Break the loop
                            }
                        });
                        if (!genreMatch) {
                            projectMatches = false;
                            if (debugMode) debugLog("No genre match for project: " + project.title);
                            return true; // Continue to next project
                        }
                    }
                    
                    // Check kind
                    if (criteria.kind && criteria.kind !== "0" && criteria.kind !== "") {
                        var kindMatch = false;
                        
                        if (debugMode) {
                            debugLog("Checking project " + project.title + " for kind: " + criteria.kind);
                            debugLog("Project kinds: ", project.kinds);
                        }
                        
                        $.each(project.kinds, function(i, kind) {
                            if (termMatches(kind, criteria.kind)) {
                                kindMatch = true;
                                if (debugMode) debugLog("Kind match found: ", kind);
                                return false; // Break the loop
                            }
                        });
                        if (!kindMatch) {
                            projectMatches = false;
                            if (debugMode) debugLog("No kind match for project: " + project.title);
                            return true; // Continue to next project
                        }
                    }
                    
                    // Check content (previously featured)
                    if (criteria.content && criteria.content !== "0" && criteria.content !== "") {
                        var contentMatch = false;
                        
                        if (debugMode) {
                            debugLog("Checking project " + project.title + " for content: " + criteria.content);
                            debugLog("Project content: ", project.content);
                        }
                        
                        $.each(project.content, function(i, content) {
                            if (termMatches(content, criteria.content)) {
                                contentMatch = true;
                                if (debugMode) debugLog("Content match found: ", content);
                                return false; // Break the loop
                            }
                        });
                        if (!contentMatch) {
                            projectMatches = false;
                            if (debugMode) debugLog("No content match for project: " + project.title);
                            return true; // Continue to next project
                        }
                    }
                    
                    // If project matches all criteria, add it to the list
                    if (projectMatches) {
                        matchingProjects.push(projectId);
                        if (debugMode) debugLog("Project matches all criteria: " + project.title);
                    }
                });
                
                debugLog("Found " + matchingProjects.length + " matching projects", null, true);
                
                // Now find locations that have these projects
                var matchingLocationIds = [];
                
                $.each(matchingProjects, function(i, projectId) {
                    var project = allProjects[projectId];
                    
                    $.each(project.locations, function(j, location) {
                        if (matchingLocationIds.indexOf(location.id) === -1) {
                            matchingLocationIds.push(location.id);
                            if (debugMode) debugLog("Adding location to matches: " + location.title);
                        }
                    });
                });
                
                debugLog("Found " + matchingLocationIds.length + " matching locations", null, true);
                
                // Update marker visibility
                var visibleCount = 0;
                var bounds = new google.maps.LatLngBounds();
                var boundsExtended = false;
                
                $.each(markers, function(i, marker) {
                    var shouldShow = matchingLocationIds.indexOf(marker.locationId) !== -1;
                    
                    // Force update marker visibility
                    marker.setVisible(shouldShow);
                    
                    if (shouldShow) {
                        visibleCount++;
                        bounds.extend(marker.getPosition());
                        boundsExtended = true;
                    }
                });
                
                // Update the visible count
                $("#visible-locations-count-" + mapId).text(visibleCount);
                
                // If no visible markers but we have criteria, show a message
                if (visibleCount === 0 && hasActiveCriteria) {
                    debugLog("No matching locations found for the current criteria", null, true);
                    
                    // If we still try to apply initial filters and get no results,
                    // try a few more times with increasing delays
                    if (!initialFilterApplied && filterAttempts < maxFilterAttempts) {
                        filterAttempts++;
                        var delay = filterAttempts * 500; // Increasing delay
                        debugLog("Retry " + filterAttempts + " in " + delay + "ms", null, true);
                        
                        setTimeout(function() {
                            filterMap(criteria);
                        }, delay);
                    }
                    
                    // Remove loading indicator if it exists
                    $("#loading-" + mapId).remove();
                    
                    return false;
                }
                
                // Fit map to bounds if we have visible markers
                if (boundsExtended) {
                    map.fitBounds(bounds);
                    
                    // Add a listener for when bounds_changed event is fired
                    google.maps.event.addListenerOnce(map, "bounds_changed", function() {
                        if (map.getZoom() > maxZoomLevel) {
                            map.setZoom(parseInt(maxZoomLevel));
                        }
                    });
                    
                    debugLog("Fitting map to bounds with " + visibleCount + " visible markers", null, true);
                }
                
                // Cache the results
                saveMapState(criteria, matchingLocationIds);
                
                // Reset filter attempts counter on successful filter
                filterAttempts = 0;
                
                // Remove loading indicator if it exists
                $("#loading-" + mapId).remove();
                
                return visibleCount > 0;
            }
            
            // Function to reset the map
            function resetMap() {
                if (!mapInitialized || !map) {
                    debugLog("Map not initialized, cannot reset", null, true);
                    return;
                }
                
                // If  in cooldown period, do not reset again
                if (resetCooldown) {
                    debugLog("Reset in cooldown, skipping");
                    return;
                }
                
                debugLog("Resetting map", null, true);
                
                // Set cooldown flag
                resetCooldown = true;
                
                // Show all markers
                $.each(markers, function(i, marker) {
                    marker.setVisible(true);
                });
                
                // Update the visible count
                $("#visible-locations-count-" + mapId).text(markers.length);
                
                // Fit map to all markers
                var bounds = new google.maps.LatLngBounds();
                $.each(markers, function(i, marker) {
                    bounds.extend(marker.getPosition());
                });
                
                map.fitBounds(bounds);
                
                // Add a listener for when bounds_changed event is fired
                google.maps.event.addListenerOnce(map, "bounds_changed", function() {
                    if (map.getZoom() > maxZoomLevel) {
                        map.setZoom(parseInt(maxZoomLevel));
                    }
                });
                
                // Close info window if open
                infoWindow.close();
                currentMarker = null;
                
                // Reset lastCriteria
                lastCriteria = null;
                
                // Reset DOM change counter
                domChangeCounter = 0;
                
                // Clear cache
                if (useCache) {
                    localStorage.removeItem(mapCacheKey);
                }
                
                // Clear cooldown after 2 seconds
                setTimeout(function() {
                    resetCooldown = false;
                }, 2000);
            }
            
            // Connect to Toolset Search
            function connectToToolsetSearch() {
                debugLog("Connecting to Toolset Search form #" + searchFormId, null, true);
                
                // Use event delegation for all form-related events
                
                // Listen for form input changes using delegated events
                $(document).on("change keyup input", "form[name=\"wpv-filter-" + searchFormId + "\"] input, form[name=\"wpv-filter-" + searchFormId + "\"] select", function() {
                    var $this = $(this);
                    debugLog("Form input changed: " + $this.attr("name") + " = " + $this.val());
                    
                    var searchForm = $("form[name=\"wpv-filter-" + searchFormId + "\"]");
                    if (searchForm.length) {
                        var criteria = getSearchCriteriaFromForm(searchForm);
                        
                        // Only filter if criteria has changed
                        if (JSON.stringify(criteria) !== lastCriteria) {
                            debouncedFilterMap(criteria);
                        }
                    }
                });
                
                // Listen for form submission
                $(document).on("submit", "form[name=\"wpv-filter-" + searchFormId + "\"]", function(e) {
                    debugLog("Form submitted");
                    
                    var searchForm = $(this);
                    var criteria = getSearchCriteriaFromForm(searchForm);
                    
                    // Only filter if criteria has changed
                    if (JSON.stringify(criteria) !== lastCriteria) {
                        debouncedFilterMap(criteria);
                    }
                });
                
                // Listen for form reset
                $(document).on("click", "form[name=\"wpv-filter-" + searchFormId + "\"] input[name=\"wpv_filter_reset\"]", function() {
                    debugLog("Form reset clicked", null, true);
                    setTimeout(function() {
                        resetMap();
                    }, 100);
                });
                
                // Listen for Toolset Search events
                $(document).on("js_event_wpv_parametric_search_results_updated", function(event, data) {
                    debugLog("Search results updated event", null, true);
                    domChangeCounter = 0; // Reset counter after successful search
                    
                    if (data.view_changed_form) {
                        var formId = data.view_changed_form.attr("data-viewid");
                        debugLog("Form ID: " + formId);
                        
                        if (formId == searchFormId) {
                            var criteria = getSearchCriteriaFromForm(data.view_changed_form);
                            
                            debugLog("Search criteria from event", criteria);
                            
                            // Only filter if criteria has changed
                            if (JSON.stringify(criteria) !== lastCriteria) {
                                debouncedFilterMap(criteria);
                            }
                        }
                    }
                });
                
                // Also listen for other Toolset events
                $(document).on("js_event_wpv_pagination_completed js_event_wpv_parametric_search_form_updated js_event_wpv_parametric_search_completed", function(event, data) {
                    debugLog("Toolset event: " + event.type);
                    reinitializeMapIfNeeded();
                    syncWithForm();
                });
                
                // Listen for Toolset search started event
                $(document).on("js_event_wpv_parametric_search_started", function(event, data) {
                    debugLog("Search started event");
                });
                
                // Set up refresh button with delegated event
                $(document).on("click", "#refresh-map-" + mapId, function() {
                    debugLog("Manual refresh clicked", null, true);
                    reinitializeMapIfNeeded();
                    syncWithForm();
                });
            }
            
            // Initialize map when Google Maps API is loaded
            if (typeof google !== "undefined" && google.maps) {
                initMap();
            } else {
                console.error("Google Maps API not loaded");
                // Try to load the map after a delay
                setTimeout(function() {
                    if (typeof google !== "undefined" && google.maps) {
                        initMap();
                    } else {
                        console.error("Google Maps API still not loaded after delay");
                    }
                }, 2000);
            }
            
            // Clean up on page unload
            $(window).on("unload", function() {
                if (syncInterval) {
                    clearInterval(syncInterval);
                }
                if (formObserver) {
                    formObserver.disconnect();
                }
            });
            
            // Re-check map initialization after AJAX events
            $(document).ajaxComplete(function() {
                debugLog("AJAX request completed");
                setTimeout(function() {
                    reinitializeMapIfNeeded();
                    syncWithForm();
                }, 500);
            });
        });
        </script>';
        
    } catch (Exception $e) {
        return '<p>Error: ' . esc_html($e->getMessage()) . '</p>';
    }
    
    return ob_get_clean();
}
add_shortcode('indonesia_map', 'indonesia_locations_map');