{"id":145,"date":"2025-12-15T10:55:50","date_gmt":"2025-12-15T07:55:50","guid":{"rendered":"https:\/\/mapsfun.com\/?p=145"},"modified":"2025-12-15T10:55:50","modified_gmt":"2025-12-15T07:55:50","slug":"best-wordpress-map-plugins-for-multiple-locations","status":"publish","type":"post","link":"https:\/\/mapsfun.com\/?p=145","title":{"rendered":"Best WordPress Map Plugins for Multiple Locations"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">How to Create a WordPress Map Plugin for Multiple Locations: A Developer&#8217;s Guide<\/h2>\n\n\n\n<p>Displaying multiple locations on your WordPress site is essential for businesses with multiple stores, service areas, or event venues. While you can build a custom plugin, it requires significant development expertise. This guide will show you how to create a functional multiple location map plugin from scratch.<\/p>\n\n\n\n<p><strong>This method requires advanced PHP\/JavaScript knowledge and a Google Cloud account with billing enabled.<\/strong><\/p>\n\n\n\n<p class=\"has-text-align-center\"><strong>Method 1: Build a Custom Multiple Locations Map Plugin<\/strong><\/p>\n\n\n\n<p>This approach creates a professional-grade solution but involves complex development work.<br><\/p>\n\n\n\n<p class=\"has-text-align-center\">Step 1: Google Maps Platform Setup<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>1.&nbsp; <strong>Create a Google Cloud Project<\/strong> at [console.cloud.google.com]<\/li>\n\n\n\n<li>2.&nbsp; <strong>Enable Billing<\/strong> (utilizing the $200 monthly credit)<\/li>\n\n\n\n<li>3.&nbsp; <strong>Enable Maps JavaScript API<\/strong> and <strong>Geocoding API<\/strong><\/li>\n\n\n\n<li>4.&nbsp; <strong>Create and restrict an API Key<\/strong> to your domain<\/li>\n<\/ul>\n\n\n\n<p><strong>Enable the necessary APIs for your custom plugin.<\/strong><\/p>\n\n\n\n<p class=\"has-text-align-center\">Step 2: Create the Custom WordPress Plugin<\/p>\n\n\n\n<p>Create a new directory `\/wp-content\/plugins\/multi-location-maps\/` with these files:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"388\" height=\"495\" src=\"https:\/\/mapsfun.com\/wp-content\/uploads\/2025\/12\/image-20.png\" alt=\"\" class=\"wp-image-146\" style=\"width:405px;height:auto\" srcset=\"https:\/\/mapsfun.com\/wp-content\/uploads\/2025\/12\/image-20.png 388w, https:\/\/mapsfun.com\/wp-content\/uploads\/2025\/12\/image-20-235x300.png 235w\" sizes=\"auto, (max-width: 388px) 100vw, 388px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"615\" height=\"420\" src=\"https:\/\/mapsfun.com\/wp-content\/uploads\/2025\/12\/image-21.png\" alt=\"\" class=\"wp-image-147\" style=\"width:438px;height:auto\" srcset=\"https:\/\/mapsfun.com\/wp-content\/uploads\/2025\/12\/image-21.png 615w, https:\/\/mapsfun.com\/wp-content\/uploads\/2025\/12\/image-21-300x205.png 300w\" sizes=\"auto, (max-width: 615px) 100vw, 615px\" \/><\/figure>\n\n\n\n<p><strong>A. Main plugin file: `multi-location-maps.php`<\/strong><\/p>\n\n\n\n<p><strong>php<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n<span class=\"hljs-comment\">\/**\n * Plugin Name: Multi Location Maps\n * Description: Advanced multiple location mapping with custom post types and shortcodes\n * Version: 2.0\n * Author: Your Name\n *\/<\/span>\n\ndefined(<span class=\"hljs-string\">'ABSPATH'<\/span>) <span class=\"hljs-keyword\">or<\/span> <span class=\"hljs-keyword\">die<\/span>(<span class=\"hljs-string\">'No direct access allowed!'<\/span>);\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">WP_Multi_Location_Maps<\/span> <\/span>{\n    \n    <span class=\"hljs-keyword\">private<\/span> $api_key;\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__construct<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;api_key = get_option(<span class=\"hljs-string\">'wmlm_api_key'<\/span>, <span class=\"hljs-string\">''<\/span>);\n        \n        add_action(<span class=\"hljs-string\">'init'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'register_location_post_type'<\/span>));\n        add_action(<span class=\"hljs-string\">'add_meta_boxes'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'add_location_meta_boxes'<\/span>));\n        add_action(<span class=\"hljs-string\">'save_post'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'save_location_metadata'<\/span>));\n        add_action(<span class=\"hljs-string\">'admin_menu'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'add_admin_menu'<\/span>));\n        add_action(<span class=\"hljs-string\">'admin_init'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'register_settings'<\/span>));\n        add_shortcode(<span class=\"hljs-string\">'wmlm_map'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'render_map_shortcode'<\/span>));\n        add_action(<span class=\"hljs-string\">'wp_enqueue_scripts'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'enqueue_scripts'<\/span>));\n        register_activation_hook(<span class=\"hljs-keyword\">__FILE__<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'activate_plugin'<\/span>));\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">activate_plugin<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        flush_rewrite_rules();\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">register_location_post_type<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        $args = <span class=\"hljs-keyword\">array<\/span>(\n            <span class=\"hljs-string\">'public'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n            <span class=\"hljs-string\">'label'<\/span>  =&gt; <span class=\"hljs-string\">'Locations'<\/span>,\n            <span class=\"hljs-string\">'supports'<\/span> =&gt; <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-string\">'title'<\/span>, <span class=\"hljs-string\">'editor'<\/span>, <span class=\"hljs-string\">'thumbnail'<\/span>),\n            <span class=\"hljs-string\">'menu_icon'<\/span> =&gt; <span class=\"hljs-string\">'dashicons-location'<\/span>,\n            <span class=\"hljs-string\">'show_in_rest'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n            <span class=\"hljs-string\">'has_archive'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>\n        );\n        register_post_type(<span class=\"hljs-string\">'location'<\/span>, $args);\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">add_location_meta_boxes<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        add_meta_box(<span class=\"hljs-string\">'location_coordinates'<\/span>, <span class=\"hljs-string\">'Location Details'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'render_coordinates_meta_box'<\/span>), <span class=\"hljs-string\">'location'<\/span>, <span class=\"hljs-string\">'normal'<\/span>, <span class=\"hljs-string\">'high'<\/span>);\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">render_coordinates_meta_box<\/span><span class=\"hljs-params\">($post)<\/span> <\/span>{\n        wp_nonce_field(<span class=\"hljs-string\">'wmlm_save_location'<\/span>, <span class=\"hljs-string\">'wmlm_nonce'<\/span>);\n        \n        $address = get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_address'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        $lat = get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_lat'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        $lng = get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_lng'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        $phone = get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_phone'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        <span class=\"hljs-meta\">?&gt;<\/span><\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"display: grid; grid-template-columns: 1fr 1fr; gap: 15px;\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Full Address:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"wmlm_address\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($address); ?&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"width: 100%;\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Enter full address for geocoding\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Phone Number:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"wmlm_phone\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($phone); ?&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"width: 100%;\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Latitude:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"wmlm_lat\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($lat); ?&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"width: 100%;\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Longitude:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"wmlm_lng\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($lng); ?&gt;\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"width: 100%;\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">small<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span>Leave latitude\/longitude empty and save to attempt automatic geocoding from address.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">small<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        <span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">save_location_metadata<\/span><span class=\"hljs-params\">($post_id)<\/span> <\/span>{\n        <span class=\"hljs-keyword\">if<\/span> (!<span class=\"hljs-keyword\">isset<\/span>($_POST&#91;<span class=\"hljs-string\">'wmlm_nonce'<\/span>]) || !wp_verify_nonce($_POST&#91;<span class=\"hljs-string\">'wmlm_nonce'<\/span>], <span class=\"hljs-string\">'wmlm_save_location'<\/span>)) {\n            <span class=\"hljs-keyword\">return<\/span>;\n        }\n        \n        <span class=\"hljs-keyword\">if<\/span> (defined(<span class=\"hljs-string\">'DOING_AUTOSAVE'<\/span>) &amp;&amp; DOING_AUTOSAVE) <span class=\"hljs-keyword\">return<\/span>;\n        \n        $fields = <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-string\">'address'<\/span>, <span class=\"hljs-string\">'lat'<\/span>, <span class=\"hljs-string\">'lng'<\/span>, <span class=\"hljs-string\">'phone'<\/span>);\n        <span class=\"hljs-keyword\">foreach<\/span> ($fields <span class=\"hljs-keyword\">as<\/span> $field) {\n            <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">isset<\/span>($_POST&#91;<span class=\"hljs-string\">'wmlm_'<\/span> . $field])) {\n                update_post_meta($post_id, <span class=\"hljs-string\">'_wmlm_'<\/span> . $field, sanitize_text_field($_POST&#91;<span class=\"hljs-string\">'wmlm_'<\/span> . $field]));\n            }\n        }\n        \n        <span class=\"hljs-comment\">\/\/ Auto-geocode if coordinates are empty but address exists<\/span>\n        $address = get_post_meta($post_id, <span class=\"hljs-string\">'_wmlm_address'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        $lat = get_post_meta($post_id, <span class=\"hljs-string\">'_wmlm_lat'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        $lng = get_post_meta($post_id, <span class=\"hljs-string\">'_wmlm_lng'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n        \n        <span class=\"hljs-keyword\">if<\/span> ($address &amp;&amp; (!$lat || !$lng) &amp;&amp; <span class=\"hljs-keyword\">$this<\/span>-&gt;api_key) {\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;geocode_address($post_id, $address);\n        }\n    }\n    \n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">geocode_address<\/span><span class=\"hljs-params\">($post_id, $address)<\/span> <\/span>{\n        $url = <span class=\"hljs-string\">'https:\/\/maps.googleapis.com\/maps\/api\/geocode\/json?address='<\/span> . urlencode($address) . <span class=\"hljs-string\">'&amp;key='<\/span> . <span class=\"hljs-keyword\">$this<\/span>-&gt;api_key;\n        \n        $response = wp_remote_get($url);\n        <span class=\"hljs-keyword\">if<\/span> (is_wp_error($response)) <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">false<\/span>;\n        \n        $data = json_decode(wp_remote_retrieve_body($response), <span class=\"hljs-keyword\">true<\/span>);\n        \n        <span class=\"hljs-keyword\">if<\/span> ($data&#91;<span class=\"hljs-string\">'status'<\/span>] === <span class=\"hljs-string\">'OK'<\/span> &amp;&amp; !<span class=\"hljs-keyword\">empty<\/span>($data&#91;<span class=\"hljs-string\">'results'<\/span>]&#91;<span class=\"hljs-number\">0<\/span>]&#91;<span class=\"hljs-string\">'geometry'<\/span>]&#91;<span class=\"hljs-string\">'location'<\/span>])) {\n            $location = $data&#91;<span class=\"hljs-string\">'results'<\/span>]&#91;<span class=\"hljs-number\">0<\/span>]&#91;<span class=\"hljs-string\">'geometry'<\/span>]&#91;<span class=\"hljs-string\">'location'<\/span>];\n            update_post_meta($post_id, <span class=\"hljs-string\">'_wmlm_lat'<\/span>, $location&#91;<span class=\"hljs-string\">'lat'<\/span>]);\n            update_post_meta($post_id, <span class=\"hljs-string\">'_wmlm_lng'<\/span>, $location&#91;<span class=\"hljs-string\">'lng'<\/span>]);\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">true<\/span>;\n        }\n        \n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">false<\/span>;\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">add_admin_menu<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        add_options_page(<span class=\"hljs-string\">'Multi Location Maps Settings'<\/span>, <span class=\"hljs-string\">'Map Settings'<\/span>, <span class=\"hljs-string\">'manage_options'<\/span>, <span class=\"hljs-string\">'wmlm-settings'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-keyword\">$this<\/span>, <span class=\"hljs-string\">'render_settings_page'<\/span>));\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">register_settings<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        register_setting(<span class=\"hljs-string\">'wmlm_settings'<\/span>, <span class=\"hljs-string\">'wmlm_api_key'<\/span>);\n        register_setting(<span class=\"hljs-string\">'wmlm_settings'<\/span>, <span class=\"hljs-string\">'wmlm_default_zoom'<\/span>);\n        register_setting(<span class=\"hljs-string\">'wmlm_settings'<\/span>, <span class=\"hljs-string\">'wmlm_map_style'<\/span>);\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">render_settings_page<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-meta\">?&gt;<\/span><\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"wrap\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>Multi Location Maps Settings<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">method<\/span>=<span class=\"hljs-string\">\"post\"<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">\"options.php\"<\/span>&gt;<\/span>\n                <span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span> settings_fields(<span class=\"hljs-string\">'wmlm_settings'<\/span>); <span class=\"hljs-meta\">?&gt;<\/span><\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">table<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"form-table\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">tr<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">th<\/span> <span class=\"hljs-attr\">scope<\/span>=<span class=\"hljs-string\">\"row\"<\/span>&gt;<\/span>Google Maps API Key<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">th<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">td<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"wmlm_api_key\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr(get_option('wmlm_api_key')); ?&gt;\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"regular-text\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">td<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">tr<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">tr<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">th<\/span> <span class=\"hljs-attr\">scope<\/span>=<span class=\"hljs-string\">\"row\"<\/span>&gt;<\/span>Default Zoom Level<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">th<\/span>&gt;<\/span>\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">td<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"number\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"wmlm_default_zoom\"<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr(get_option('wmlm_default_zoom', '10')); ?&gt;\"<\/span> <span class=\"hljs-attr\">min<\/span>=<span class=\"hljs-string\">\"1\"<\/span> <span class=\"hljs-attr\">max<\/span>=<span class=\"hljs-string\">\"20\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">td<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">tr<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">table<\/span>&gt;<\/span>\n                <span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span> submit_button(); <span class=\"hljs-meta\">?&gt;<\/span><\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">enqueue_scripts<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-keyword\">global<\/span> $post;\n        <span class=\"hljs-keyword\">if<\/span> (is_a($post, <span class=\"hljs-string\">'WP_Post'<\/span>) &amp;&amp; has_shortcode($post-&gt;post_content, <span class=\"hljs-string\">'wmlm_map'<\/span>)) {\n            wp_enqueue_script(<span class=\"hljs-string\">'google-maps'<\/span>, <span class=\"hljs-string\">'https:\/\/maps.googleapis.com\/maps\/api\/js?key='<\/span> . <span class=\"hljs-keyword\">$this<\/span>-&gt;api_key . <span class=\"hljs-string\">'&amp;loading=async'<\/span>, <span class=\"hljs-keyword\">array<\/span>(), <span class=\"hljs-keyword\">null<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n            wp_enqueue_script(<span class=\"hljs-string\">'wmlm-frontend'<\/span>, plugin_dir_url(<span class=\"hljs-keyword\">__FILE__<\/span>) . <span class=\"hljs-string\">'assets\/frontend.js'<\/span>, <span class=\"hljs-keyword\">array<\/span>(<span class=\"hljs-string\">'jquery'<\/span>), <span class=\"hljs-string\">'1.0'<\/span>, <span class=\"hljs-keyword\">true<\/span>);\n            \n            <span class=\"hljs-comment\">\/\/ Pass data to JavaScript<\/span>\n            $locations = <span class=\"hljs-keyword\">$this<\/span>-&gt;get_all_locations();\n            wp_localize_script(<span class=\"hljs-string\">'wmlm-frontend'<\/span>, <span class=\"hljs-string\">'wmlmData'<\/span>, <span class=\"hljs-keyword\">array<\/span>(\n                <span class=\"hljs-string\">'locations'<\/span> =&gt; $locations,\n                <span class=\"hljs-string\">'defaultZoom'<\/span> =&gt; get_option(<span class=\"hljs-string\">'wmlm_default_zoom'<\/span>, <span class=\"hljs-number\">10<\/span>),\n                <span class=\"hljs-string\">'mapStyle'<\/span> =&gt; get_option(<span class=\"hljs-string\">'wmlm_map_style'<\/span>, <span class=\"hljs-string\">'&#91;]'<\/span>)\n            ));\n            \n            wp_enqueue_style(<span class=\"hljs-string\">'wmlm-styles'<\/span>, plugin_dir_url(<span class=\"hljs-keyword\">__FILE__<\/span>) . <span class=\"hljs-string\">'assets\/styles.css'<\/span>, <span class=\"hljs-keyword\">array<\/span>(), <span class=\"hljs-string\">'1.0'<\/span>);\n        }\n    }\n    \n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">get_all_locations<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n        $locations = <span class=\"hljs-keyword\">array<\/span>();\n        $posts = get_posts(<span class=\"hljs-keyword\">array<\/span>(\n            <span class=\"hljs-string\">'post_type'<\/span> =&gt; <span class=\"hljs-string\">'location'<\/span>,\n            <span class=\"hljs-string\">'numberposts'<\/span> =&gt; <span class=\"hljs-number\">-1<\/span>,\n            <span class=\"hljs-string\">'post_status'<\/span> =&gt; <span class=\"hljs-string\">'publish'<\/span>\n        ));\n        \n        <span class=\"hljs-keyword\">foreach<\/span> ($posts <span class=\"hljs-keyword\">as<\/span> $post) {\n            $locations&#91;] = <span class=\"hljs-keyword\">array<\/span>(\n                <span class=\"hljs-string\">'id'<\/span> =&gt; $post-&gt;ID,\n                <span class=\"hljs-string\">'title'<\/span> =&gt; $post-&gt;post_title,\n                <span class=\"hljs-string\">'description'<\/span> =&gt; wp_trim_words($post-&gt;post_content, <span class=\"hljs-number\">25<\/span>),\n                <span class=\"hljs-string\">'address'<\/span> =&gt; get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_address'<\/span>, <span class=\"hljs-keyword\">true<\/span>),\n                <span class=\"hljs-string\">'phone'<\/span> =&gt; get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_phone'<\/span>, <span class=\"hljs-keyword\">true<\/span>),\n                <span class=\"hljs-string\">'lat'<\/span> =&gt; floatval(get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_lat'<\/span>, <span class=\"hljs-keyword\">true<\/span>)),\n                <span class=\"hljs-string\">'lng'<\/span> =&gt; floatval(get_post_meta($post-&gt;ID, <span class=\"hljs-string\">'_wmlm_lng'<\/span>, <span class=\"hljs-keyword\">true<\/span>)),\n                <span class=\"hljs-string\">'thumbnail'<\/span> =&gt; get_the_post_thumbnail_url($post-&gt;ID, <span class=\"hljs-string\">'thumbnail'<\/span>)\n            );\n        }\n        \n        <span class=\"hljs-keyword\">return<\/span> $locations;\n    }\n    \n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">render_map_shortcode<\/span><span class=\"hljs-params\">($atts)<\/span> <\/span>{\n        $atts = shortcode_atts(<span class=\"hljs-keyword\">array<\/span>(\n            <span class=\"hljs-string\">'height'<\/span> =&gt; <span class=\"hljs-string\">'500px'<\/span>,\n            <span class=\"hljs-string\">'width'<\/span> =&gt; <span class=\"hljs-string\">'100%'<\/span>,\n            <span class=\"hljs-string\">'zoom'<\/span> =&gt; <span class=\"hljs-string\">'auto'<\/span>,\n            <span class=\"hljs-string\">'cluster'<\/span> =&gt; <span class=\"hljs-string\">'true'<\/span>\n        ), $atts);\n        \n        ob_start();\n        <span class=\"hljs-meta\">?&gt;<\/span><\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"wmlm-map-container\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"wmlm-map\"<\/span> \n                 <span class=\"hljs-attr\">data-height<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($atts&#91;'height']); ?&gt;\"<\/span>\n                 <span class=\"hljs-attr\">data-width<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($atts&#91;'width']); ?&gt;\"<\/span>\n                 <span class=\"hljs-attr\">data-zoom<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($atts&#91;'zoom']); ?&gt;\"<\/span>\n                 <span class=\"hljs-attr\">data-cluster<\/span>=<span class=\"hljs-string\">\"&lt;?php echo esc_attr($atts&#91;'cluster']); ?&gt;\"<\/span>\n                 <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"height: &lt;?php echo esc_attr($atts&#91;'height']); ?&gt;; width: &lt;?php echo esc_attr($atts&#91;'width']); ?&gt;;\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"wmlm-locations-list\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n        <span class=\"hljs-keyword\">return<\/span> ob_get_clean();\n    }\n}\n\n<span class=\"hljs-keyword\">new<\/span> WP_Multi_Location_Maps();\n<span class=\"hljs-meta\">?&gt;<\/span><\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>B. Create frontend JavaScript: `\/assets\/frontend.js<\/strong><\/p>\n\n\n\n<p><strong>javascript<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">$<\/span>) <\/span>{\n    <span class=\"hljs-string\">'use strict'<\/span>;\n    \n    <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MultiLocationMap<\/span> <\/span>{\n        <span class=\"hljs-keyword\">constructor<\/span>(container) {\n            <span class=\"hljs-keyword\">this<\/span>.container = container;\n            <span class=\"hljs-keyword\">this<\/span>.map = <span class=\"hljs-literal\">null<\/span>;\n            <span class=\"hljs-keyword\">this<\/span>.markers = &#91;];\n            <span class=\"hljs-keyword\">this<\/span>.infoWindow = <span class=\"hljs-literal\">null<\/span>;\n            <span class=\"hljs-keyword\">this<\/span>.settings = {\n                <span class=\"hljs-attr\">height<\/span>: container.data(<span class=\"hljs-string\">'height'<\/span>) || <span class=\"hljs-string\">'500px'<\/span>,\n                <span class=\"hljs-attr\">width<\/span>: container.data(<span class=\"hljs-string\">'width'<\/span>) || <span class=\"hljs-string\">'100%'<\/span>,\n                <span class=\"hljs-attr\">zoom<\/span>: container.data(<span class=\"hljs-string\">'zoom'<\/span>) || <span class=\"hljs-string\">'auto'<\/span>,\n                <span class=\"hljs-attr\">cluster<\/span>: container.data(<span class=\"hljs-string\">'cluster'<\/span>) === <span class=\"hljs-string\">'true'<\/span>\n            };\n            \n            <span class=\"hljs-keyword\">this<\/span>.init();\n        }\n        \n        init() {\n            <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">typeof<\/span> google === <span class=\"hljs-string\">'undefined'<\/span>) {\n                <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Google Maps API not loaded'<\/span>);\n                <span class=\"hljs-keyword\">return<\/span>;\n            }\n            \n            <span class=\"hljs-keyword\">this<\/span>.createMap();\n            <span class=\"hljs-keyword\">this<\/span>.addMarkers();\n            <span class=\"hljs-keyword\">this<\/span>.addListings();\n        }\n        \n        createMap() {\n            <span class=\"hljs-keyword\">const<\/span> center = <span class=\"hljs-keyword\">this<\/span>.calculateCenter();\n            \n            <span class=\"hljs-keyword\">this<\/span>.map = <span class=\"hljs-keyword\">new<\/span> google.maps.Map(<span class=\"hljs-keyword\">this<\/span>.container&#91;<span class=\"hljs-number\">0<\/span>], {\n                <span class=\"hljs-attr\">zoom<\/span>: <span class=\"hljs-keyword\">this<\/span>.settings.zoom === <span class=\"hljs-string\">'auto'<\/span> ? <span class=\"hljs-number\">10<\/span> : <span class=\"hljs-built_in\">parseInt<\/span>(<span class=\"hljs-keyword\">this<\/span>.settings.zoom),\n                <span class=\"hljs-attr\">center<\/span>: center,\n                <span class=\"hljs-attr\">styles<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.parse(wmlmData.mapStyle || <span class=\"hljs-string\">'&#91;]'<\/span>),\n                <span class=\"hljs-attr\">mapTypeControl<\/span>: <span class=\"hljs-literal\">false<\/span>,\n                <span class=\"hljs-attr\">streetViewControl<\/span>: <span class=\"hljs-literal\">true<\/span>,\n                <span class=\"hljs-attr\">fullscreenControl<\/span>: <span class=\"hljs-literal\">true<\/span>\n            });\n            \n            <span class=\"hljs-keyword\">this<\/span>.infoWindow = <span class=\"hljs-keyword\">new<\/span> google.maps.InfoWindow();\n        }\n        \n        calculateCenter() {\n            <span class=\"hljs-keyword\">if<\/span> (wmlmData.locations.length === <span class=\"hljs-number\">0<\/span>) {\n                <span class=\"hljs-keyword\">return<\/span> { <span class=\"hljs-attr\">lat<\/span>: <span class=\"hljs-number\">40.7128<\/span>, <span class=\"hljs-attr\">lng<\/span>: <span class=\"hljs-number\">-74.0060<\/span> }; <span class=\"hljs-comment\">\/\/ Default to NYC<\/span>\n            }\n            \n            <span class=\"hljs-keyword\">const<\/span> bounds = <span class=\"hljs-keyword\">new<\/span> google.maps.LatLngBounds();\n            wmlmData.locations.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">location<\/span> =&gt;<\/span> {\n                bounds.extend(<span class=\"hljs-keyword\">new<\/span> google.maps.LatLng(location.lat, location.lng));\n            });\n            \n            <span class=\"hljs-keyword\">return<\/span> bounds.getCenter();\n        }\n        \n        addMarkers() {\n            <span class=\"hljs-keyword\">const<\/span> bounds = <span class=\"hljs-keyword\">new<\/span> google.maps.LatLngBounds();\n            \n            wmlmData.locations.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">location<\/span> =&gt;<\/span> {\n                <span class=\"hljs-keyword\">const<\/span> marker = <span class=\"hljs-keyword\">new<\/span> google.maps.Marker({\n                    <span class=\"hljs-attr\">position<\/span>: { <span class=\"hljs-attr\">lat<\/span>: location.lat, <span class=\"hljs-attr\">lng<\/span>: location.lng },\n                    <span class=\"hljs-attr\">map<\/span>: <span class=\"hljs-keyword\">this<\/span>.map,\n                    <span class=\"hljs-attr\">title<\/span>: location.title,\n                    <span class=\"hljs-attr\">icon<\/span>: <span class=\"hljs-keyword\">this<\/span>.getCustomIcon()\n                });\n                \n                <span class=\"hljs-keyword\">this<\/span>.markers.push(marker);\n                bounds.extend(marker.getPosition());\n                \n                <span class=\"hljs-comment\">\/\/ Add click listener<\/span>\n                marker.addListener(<span class=\"hljs-string\">'click'<\/span>, () =&gt; {\n                    <span class=\"hljs-keyword\">this<\/span>.showInfoWindow(marker, location);\n                });\n            });\n            \n            <span class=\"hljs-comment\">\/\/ Fit map to show all markers<\/span>\n            <span class=\"hljs-keyword\">if<\/span> (wmlmData.locations.length &gt; <span class=\"hljs-number\">1<\/span> &amp;&amp; <span class=\"hljs-keyword\">this<\/span>.settings.zoom === <span class=\"hljs-string\">'auto'<\/span>) {\n                <span class=\"hljs-keyword\">this<\/span>.map.fitBounds(bounds);\n            }\n        }\n        \n        getCustomIcon() {\n            <span class=\"hljs-comment\">\/\/ Return custom marker icon or null for default<\/span>\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">null<\/span>;\n        }\n        \n        showInfoWindow(marker, location) {\n            <span class=\"hljs-keyword\">const<\/span> content = <span class=\"hljs-string\">`\n                &lt;div class=\"wmlm-info-window\"&gt;\n                    <span class=\"hljs-subst\">${location.thumbnail ? <span class=\"hljs-string\">`&lt;img src=\"<span class=\"hljs-subst\">${location.thumbnail}<\/span>\" alt=\"<span class=\"hljs-subst\">${location.title}<\/span>\" style=\"max-width: 100px; float: left; margin-right: 10px;\"&gt;`<\/span> : <span class=\"hljs-string\">''<\/span>}<\/span>\n                    &lt;h3 style=\"margin: 0 0 8px 0;\"&gt;<span class=\"hljs-subst\">${location.title}<\/span>&lt;\/h3&gt;\n                    &lt;p style=\"margin: 0 0 5px 0; color: #666;\"&gt;<span class=\"hljs-subst\">${location.address}<\/span>&lt;\/p&gt;\n                    <span class=\"hljs-subst\">${location.phone ? <span class=\"hljs-string\">`&lt;p style=\"margin: 0 0 5px 0;\"&gt;\ud83d\udcde <span class=\"hljs-subst\">${location.phone}<\/span>&lt;\/p&gt;`<\/span> : <span class=\"hljs-string\">''<\/span>}<\/span>\n                    &lt;p style=\"margin: 0; font-size: 14px;\"&gt;<span class=\"hljs-subst\">${location.description}<\/span>&lt;\/p&gt;\n                    &lt;div style=\"clear: both;\"&gt;&lt;\/div&gt;\n                &lt;\/div&gt;\n            `<\/span>;\n            \n            <span class=\"hljs-keyword\">this<\/span>.infoWindow.setContent(content);\n            <span class=\"hljs-keyword\">this<\/span>.infoWindow.open(<span class=\"hljs-keyword\">this<\/span>.map, marker);\n        }\n        \n        addListings() {\n            <span class=\"hljs-keyword\">const<\/span> $list = $(<span class=\"hljs-string\">'.wmlm-locations-list'<\/span>);\n            <span class=\"hljs-keyword\">if<\/span> ($list.length === <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">return<\/span>;\n            \n            <span class=\"hljs-keyword\">let<\/span> html = <span class=\"hljs-string\">'&lt;div class=\"wmlm-locations\"&gt;'<\/span>;\n            wmlmData.locations.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">location<\/span> =&gt;<\/span> {\n                html += <span class=\"hljs-string\">`\n                    &lt;div class=\"wmlm-location-item\" data-lat=\"<span class=\"hljs-subst\">${location.lat}<\/span>\" data-lng=\"<span class=\"hljs-subst\">${location.lng}<\/span>\"&gt;\n                        &lt;h4&gt;<span class=\"hljs-subst\">${location.title}<\/span>&lt;\/h4&gt;\n                        &lt;p class=\"address\"&gt;<span class=\"hljs-subst\">${location.address}<\/span>&lt;\/p&gt;\n                        <span class=\"hljs-subst\">${location.phone ? <span class=\"hljs-string\">`&lt;p class=\"phone\"&gt;<span class=\"hljs-subst\">${location.phone}<\/span>&lt;\/p&gt;`<\/span> : <span class=\"hljs-string\">''<\/span>}<\/span>\n                    &lt;\/div&gt;\n                `<\/span>;\n            });\n            html += <span class=\"hljs-string\">'&lt;\/div&gt;'<\/span>;\n            \n            $list.html(html);\n            \n            <span class=\"hljs-comment\">\/\/ Add click handlers for listings<\/span>\n            $(<span class=\"hljs-string\">'.wmlm-location-item'<\/span>).on(<span class=\"hljs-string\">'click'<\/span>, (e) =&gt; {\n                <span class=\"hljs-keyword\">const<\/span> $item = $(e.currentTarget);\n                <span class=\"hljs-keyword\">const<\/span> lat = <span class=\"hljs-built_in\">parseFloat<\/span>($item.data(<span class=\"hljs-string\">'lat'<\/span>));\n                <span class=\"hljs-keyword\">const<\/span> lng = <span class=\"hljs-built_in\">parseFloat<\/span>($item.data(<span class=\"hljs-string\">'lng'<\/span>));\n                \n                <span class=\"hljs-keyword\">this<\/span>.map.panTo({ lat, lng });\n                <span class=\"hljs-keyword\">this<\/span>.map.setZoom(<span class=\"hljs-number\">15<\/span>);\n            });\n        }\n    }\n    \n    <span class=\"hljs-comment\">\/\/ Initialize maps when document is ready<\/span>\n    $(<span class=\"hljs-built_in\">document<\/span>).ready(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n        $(<span class=\"hljs-string\">'.wmlm-map'<\/span>).each(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n            <span class=\"hljs-keyword\">new<\/span> MultiLocationMap($(<span class=\"hljs-keyword\">this<\/span>));\n        });\n    });\n    \n})(jQuery);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>C. Create CSS file: `\/assets\/styles.css<\/strong><\/p>\n\n\n\n<p><strong>css<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.wmlm-map-container<\/span> {\n    <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">20px<\/span> <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">8px<\/span>;\n    <span class=\"hljs-attribute\">overflow<\/span>: hidden;\n    <span class=\"hljs-attribute\">box-shadow<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">4px<\/span> <span class=\"hljs-number\">12px<\/span> <span class=\"hljs-built_in\">rgba<\/span>(<span class=\"hljs-number\">0<\/span>,<span class=\"hljs-number\">0<\/span>,<span class=\"hljs-number\">0<\/span>,<span class=\"hljs-number\">0.1<\/span>);\n}\n\n<span class=\"hljs-selector-class\">.wmlm-map<\/span> {\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">8px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-locations<\/span> {\n    <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">20px<\/span>;\n    <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-number\">#f9f9f9<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-location-item<\/span> {\n    <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">15px<\/span>;\n    <span class=\"hljs-attribute\">margin-bottom<\/span>: <span class=\"hljs-number\">10px<\/span>;\n    <span class=\"hljs-attribute\">background<\/span>: white;\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">6px<\/span>;\n    <span class=\"hljs-attribute\">cursor<\/span>: pointer;\n    <span class=\"hljs-attribute\">transition<\/span>: all <span class=\"hljs-number\">0.3s<\/span> ease;\n    <span class=\"hljs-attribute\">border-left<\/span>: <span class=\"hljs-number\">4px<\/span> solid <span class=\"hljs-number\">#4CAF50<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-location-item<\/span><span class=\"hljs-selector-pseudo\">:hover<\/span> {\n    <span class=\"hljs-attribute\">transform<\/span>: <span class=\"hljs-built_in\">translateX<\/span>(<span class=\"hljs-number\">5px<\/span>);\n    <span class=\"hljs-attribute\">box-shadow<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">2px<\/span> <span class=\"hljs-number\">8px<\/span> <span class=\"hljs-built_in\">rgba<\/span>(<span class=\"hljs-number\">0<\/span>,<span class=\"hljs-number\">0<\/span>,<span class=\"hljs-number\">0<\/span>,<span class=\"hljs-number\">0.1<\/span>);\n}\n\n<span class=\"hljs-selector-class\">.wmlm-location-item<\/span> <span class=\"hljs-selector-tag\">h4<\/span> {\n    <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">8px<\/span> <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#2c3e50<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-location-item<\/span> <span class=\"hljs-selector-class\">.address<\/span> {\n    <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">5px<\/span> <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#666<\/span>;\n    <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">14px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-location-item<\/span> <span class=\"hljs-selector-class\">.phone<\/span> {\n    <span class=\"hljs-attribute\">margin<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#4CAF50<\/span>;\n    <span class=\"hljs-attribute\">font-weight<\/span>: bold;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-info-window<\/span> {\n    <span class=\"hljs-attribute\">max-width<\/span>: <span class=\"hljs-number\">300px<\/span>;\n    <span class=\"hljs-attribute\">font-family<\/span>: Arial, sans-serif;\n}\n\n<span class=\"hljs-selector-class\">.wmlm-info-window<\/span> <span class=\"hljs-selector-tag\">img<\/span> {\n    <span class=\"hljs-attribute\">border-radius<\/span>: <span class=\"hljs-number\">4px<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"has-text-align-center\">Step 3: Install and Use the Plugin<\/p>\n\n\n\n<p>1.&nbsp; <strong>Zip the plugin<\/strong> folder and upload via WordPress Admin<\/p>\n\n\n\n<p>2.&nbsp;<strong> Activate the plugin<\/strong> and configure your API key in Settings &gt; Map Settings<\/p>\n\n\n\n<p>3.&nbsp; <strong>Add locations <\/strong>through the new Locations menu<\/p>\n\n\n\n<p>4.&nbsp; <strong>Use shortcode<\/strong> `[wmlm_map]` on any page or post<\/p>\n\n\n\n<p><strong>Manage locations through a custom post type with detailed metadata<\/strong><\/p>\n\n\n\n<p class=\"has-text-align-center\"><strong>The Challenges of This Custom Plugin Development<\/strong><\/p>\n\n\n\n<p>While this solution offers complete control, it comes with significant challenges:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Complex Development:<\/strong> Requires advanced PHP, JavaScript, and WordPress API knowledge<\/li>\n\n\n\n<li><strong>Maintenance Burden:<\/strong> You&#8217;re responsible for security updates, WordPress compatibility, and bug fixes<\/li>\n\n\n\n<li><strong>Limited Geocoding:<\/strong> Basic geocoding implementation without error handling or batch processing<\/li>\n\n\n\n<li><strong>No Advanced Features:<\/strong> Missing marker clustering, category filters, live search, or import\/export functionality<\/li>\n\n\n\n<li><strong>API Management Complexity:<\/strong> Requires ongoing monitoring of Google Cloud usage, quotas, and costs<\/li>\n\n\n\n<li><strong>Time Investment:<\/strong> Development, testing, and debugging can take weeks of dedicated work<\/li>\n\n\n\n<li><strong>No Support:<\/strong> You&#8217;re on your own for troubleshooting and feature requests<\/li>\n<\/ol>\n\n\n\n<p class=\"has-text-align-center\"><strong>Get a Professional Multi-Location Map in Minutes with MapsFun.com<\/strong><\/p>\n\n\n\n<p>Why spend weeks developing and maintaining a complex plugin when you can have a superior solution instantly?<\/p>\n\n\n<p><iframe src=\"https:\/\/panel.mapsfun.com\/embed-map?code=668ecbcced7931f89205d1e881bb82aa&#038;lang=uk&#038;tpl=photo\" width=\"100%\" height=\"600\" style=\"border:0\" loading=\"lazy\" referrerpolicy=\"no-referrer-when-downgrade\"><\/iframe><\/p>\n\n\n\n<p><a href=\"http:\/\/mapsfun.com\">MapsFun.com<\/a> offers the ultimate WordPress multiple location mapping solution:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>1.&nbsp; <strong>Zero Coding Required:<\/strong> Create stunning, feature-rich maps with our visual editor\u2014no development needed<\/li>\n\n\n\n<li>2.&nbsp; <strong>Automatic Geocoding:<\/strong> Simply enter addresses\u2014we handle all coordinate processing automatically<\/li>\n\n\n\n<li>3.&nbsp;<strong> Advanced Features: <\/strong>Marker clustering, category filters, live search, custom styling, and import\/export<\/li>\n\n\n\n<li>4.&nbsp;<strong> Easy WordPress Integration:<\/strong> Simple plugin installation with seamless WordPress integration<\/li>\n\n\n\n<li>5.&nbsp; <strong>Fully Managed: <\/strong>No API key management, no security concerns, automatic updates and support<\/li>\n\n\n\n<li>6.&nbsp; <strong>Professional Support:<\/strong> Get help when you need it from our dedicated support team<\/li>\n<\/ul>\n\n\n\n<p>Stop wasting time on complex development and maintenance. Create a professional, feature-rich multi-location map in just a few clicks at <a href=\"http:\/\/mapsfun.com\">MapsFun.com<\/a>.&nbsp;<br><\/p>\n","protected":false},"excerpt":{"rendered":"How to Create a WordPress Map Plugin for Multiple Locations: A Developer&#8217;s Guide Displaying multiple locations on your WordPress site is essential for businesses with multiple stores, service areas, or event venues. While you can build a custom plugin, it requires significant development expertise. This guide will show you how to create a functional multiple [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20,1],"tags":[],"class_list":["post-145","post","type-post","status-publish","format-standard","hentry","category-wordpress-map-plugin-multiple-locations","category-1"],"_links":{"self":[{"href":"https:\/\/mapsfun.com\/index.php?rest_route=\/wp\/v2\/posts\/145","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mapsfun.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mapsfun.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mapsfun.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mapsfun.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=145"}],"version-history":[{"count":2,"href":"https:\/\/mapsfun.com\/index.php?rest_route=\/wp\/v2\/posts\/145\/revisions"}],"predecessor-version":[{"id":206,"href":"https:\/\/mapsfun.com\/index.php?rest_route=\/wp\/v2\/posts\/145\/revisions\/206"}],"wp:attachment":[{"href":"https:\/\/mapsfun.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=145"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mapsfun.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=145"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mapsfun.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=145"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}