From a9654f00c8fbc9934289d761843ddd071025013f Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 28 Aug 2026 17:20:28 +0000
Subject: [PATCH] =PHPStorm Code Inspection: fixed class reference errors (from getting rid of JVB() and replacing with direct class::getInstance; as well as general code sniffing, etc
---
inc/integrations/services/GoogleMaps.php | 985 +++++++++++++++------------------------------------------
1 files changed, 264 insertions(+), 721 deletions(-)
diff --git a/inc/integrations/services/GoogleMaps.php b/inc/integrations/services/GoogleMaps.php
index 6477a0d..adfa358 100644
--- a/inc/integrations/services/GoogleMaps.php
+++ b/inc/integrations/services/GoogleMaps.php
@@ -5,8 +5,10 @@
*/
namespace JVBase\integrations\services;
+use JVBase\integrations\Admin;
+use JVBase\Instance;
use JVBase\integrations\Integrations;
-use JVBase\registrar\Registrar;
+use JVBase\integrations\Requests;
if (!defined('ABSPATH')) {
exit;
@@ -14,468 +16,101 @@
class GoogleMaps extends Integrations
{
- private string $public_key;
- private string $private_key;
- private string $map_id;
- private array $enabled_apis = [];
-
- // Edmonton default coordinates
- private const DEFAULT_LAT = 53.5461;
- private const DEFAULT_LNG = -113.4938;
- private const DEFAULT_ZOOM = 11; // City-wide view
- private const DEFAULT_MAP_ID = '873aed4ca260b4203e45b6e3';
-
- protected static self $instance;
- public static function getInstance():self
- {
-
- if (!isset(self::$instance)) {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
+ use Admin, Instance, Requests;
protected function __construct()
{
$this->service_name = 'maps';
$this->title = 'Google Maps';
$this->icon = 'map-pin';
+
$this->apiBase = 'https://maps.googleapis.com/maps/api/';
- $this->apiEndpoints = [
- 'geocode/json',
- 'place/details/json',
- 'place/nearbysearch/json',
- 'distancematrix/json',
- 'js'
- ];
+ $this->defineFields();
+ $this->defineInstructions();
+ $this->defineActions();
- $this->fields = [
- 'public_key' => [
- 'type' => 'text',
- 'subtype' => 'password',
- 'label' => 'Maps Platform API Key (Referrer-Restricted)',
- 'hint' => 'Restrict this key by <b>HTTP referrers</b> for security. Used for displaying maps in browsers.',
- 'required' => true,
- ],
- 'private_key' => [
- 'type' => 'text',
- 'subtype' => 'password',
- 'label' => 'Geocoding & Places API Key (IP-Restricted)',
- 'hint' => 'Restrict this key by <br>IP Addresses</b>. Used for geocoding and server-side operations.',
- 'required' => true,
- ]
- ];
- $this->advanced = [
- 'map_id' => [
- 'type' => 'text',
- 'label' => 'Map ID',
- 'hint' => 'Required for custom styling. Create a Map ID in <a href="https://console.cloud.google.com/google/maps-apis/studio/maps" target="_blank">Google Cloud Console → Maps → Map Management</a>'
- ],
- 'default_zoom' => [
- 'type' => 'text',
- 'subtype' => 'number',
- 'min' => 1,
- 'max' => 20,
- 'label' => 'Default Zoom',
- 'default' => 11
- ],
- ];
- $this->instructions = [
- 'Create two API keys in <a href="https://console.cloud.google.com/apis/credentials" target="_blank">Google Cloud Console</a>',
- 'Enable <strong>Geocoding API + Places API</strong> for frontend key, restricted by your server IP address',
- 'Enable <strong>Maps Platform API</strong> for server key, restricted by your website domain',
- ];
- $this->defaults = [
-
- ];
parent::__construct();
-
- $this->actions = array_merge(
- $this->actions,
- [
- 'reverse_geocode' => 'processReverseGeocode',
- 'geocode' => 'processGeocode'
- ]
- );
// Auto-enqueue maps script when needed
add_action('wp_enqueue_scripts', [$this, 'maybeEnqueueMapsScript']);
-// add_action('admin_enqueue_scripts', [$this, 'maybeEnqueueMapsScript']);
}
+ protected function defineFields():void
+ {
+ $pub = $this->addField('public_key', 'Maps Platform API Key', 'text', 'manage_options');
+ $pub->setHint('Restrict this key by HTTP referrers for security. Used for displaying maps in browsers.');
+ $pub->setSubType('password');
+ $pub->setRequired();
+ $priv = $this->addField('private_key', 'Geocoding & Places API Key', 'text', 'manage_options');
+ $priv->setHint('Restrict this key by IP Addresses. Used for geocoding and server-side operations.');
+ $priv->setRequired();
+ $priv->setSubType('password');
+
+ $map_id = $this->addField('map_id', 'Map ID', 'text', 'manage_options');
+ $map_id->setHint('Required for custom styling. Create a Map ID in Google Cloud Console -> Maps -> Map Management.');
+
+ $zoom = $this->addField('default_zoom', 'Default Zoom', 'text', 'manage_options');
+ $zoom->setSubType('number');
+ $zoom->setDefault(11);
+ $zoom->setMin(1);
+ $zoom->setMax(20);
+
+ $lng = $this->addField('default_lng', 'Default Longitude', 'text', 'manage_options');
+ $lng->setSubType('number');
+ $lng->setDefault(-113.4938);
+
+ $lat = $this->addField('default_lat', 'Default Latitude', 'text', 'manage_options');
+ $lng->setSubType('number');
+ $lng->setDefault(53.5461);
+ }
+
+ protected function defineInstructions():void
+ {
+ $this->instructions = [
+ 'Create two API keys in <a href="https://console.cloud.google.com/apis/credentials" target="_blank">Google Cloud Console</a>',
+ 'Enable <strong>Geocoding API + Places API</strong> for frontend key, restricted by your server IP address',
+ 'Enable <strong>Maps Platform API</strong> for server key, restricted by your website domain',
+ ];
+ }
+
+ protected function defineActions():void
+ {
+ $this->addAction(
+ 'reverse_geocode',
+ [$this, 'processReverseGeocode']
+ );
+
+ $this->addAction(
+ 'geocode',
+ [$this, 'processGeocode']
+ );
+ }
+ public function processReverseGeocode(array $data):array
+ {
+ if (!array_key_exists('lat', $data) || !array_key_exists('lng', $data)) {
+ return $this->response(false, 'Invalid coordinates provided', $data);
+ }
+ $lat = floatval($data['lat']);
+ $lng = floatval($data['lng']);
+
+ $result = $this->reverseGeocode($lat, $lng);
+ if ($result) {
+ return $this->response(true, 'Successfully reversed Geocode', ['data'=> $result]);
+ }
+ return $this->response(false, 'Reverse geocoding failed');
+ }
+
+ public function processGeocode(array $data):array
+ {
+ if (!array_key_exists('address', $data)) {
+ return $this->response(false, 'No address provided');
+ }
+ $result = $this->geocodeAddress(sanitize_text_field($data['address']));
+ if ($result) {
+ return $this->response(true, 'Successfully geocoded address', ['data' => $result]);
+ }
+ return $this->response(false, 'Geocoding failed');
+ }
protected function initialize(): void
{
- $this->private_key = $this->credentials['private_key'] ?? '';
- $this->public_key = $this->credentials['public_key'] ?? '';
- $this->map_id = $this->credentials['map_id'] ?? self::DEFAULT_MAP_ID;
- $this->rate_limits = ['min_interval' => 0]; // Google Maps has generous limits
- }
-
- /**
- * Get service description
- */
- public function getServiceDescription(): string
- {
- return "Enable location features and embedded maps throughout your site.";
- }
-
- /**
- * Get request headers (required by base class)
- */
- protected function getRequestHeaders(): array
- {
- // Google Maps API uses API key in URL params, not headers
- return [
- 'Accept' => 'application/json',
- 'Content-Type' => 'application/json'
- ];
- }
-
-
- /**
- * Process test connection request
- */
- protected function performConnectionTest(): bool
- {
- if (!$this->isSetUp()) {
- return false;
- }
-
- // Test with a simple geocoding request
- $test_address = 'Edmonton, Alberta, Canada';
- $result = $this->geocodeAddress($test_address);
-
- return $result !== null;
- }
-
- /**
- * Process reverse geocode request
- */
- private function processReverseGeocode(array $data): WP_Error|array
- {
- $lat = floatval($data['lat'] ?? 0);
- $lng = floatval($data['lng'] ?? 0);
-
- if (!$lat || !$lng) {
- return new WP_Error('invalid_coordinates', 'Invalid coordinates provided');
- }
-
- $result = $this->reverseGeocode($lat, $lng);
-
- if ($result) {
- return ['success' => true, 'data' => $result];
- }
-
- return new WP_Error('geocoding_failed', 'Reverse geocoding failed');
- }
-
- /**
- * Process geocode request
- */
- private function processGeocode(array $data): WP_Error|array
- {
- $address = sanitize_text_field($data['address'] ?? '');
-
- if (empty($address)) {
- return new WP_Error('invalid_address', 'No address provided');
- }
-
- $result = $this->geocodeAddress($address);
-
- if ($result) {
- return ['success' => true, 'data' => $result];
- }
-
- return new WP_Error('geocoding_failed', 'Geocoding failed');
- }
-
- /**
- * Geocode an address to coordinates
- */
- public function geocodeAddress(string $address): ?array
- {
- if (!$this->isSetUp()) {
- return null;
- }
-
- $params = [
- 'address' => $address,
- 'key' => $this->private_key
- ];
-
- $key = $this->cache->generateKey(['geocode', $address]);
- $cached = $this->cache->get($key);
- if ($cached) {
- return $cached;
- }
-
- $url = $this->apiBase . 'geocode/json?' . http_build_query($params);
- $response = wp_remote_get($url);
-
- if (is_wp_error($response)) {
- $this->logError('Geocoding request failed', [
- 'error' => $response->get_error_message(),
- 'address' => $address
- ]);
- return null;
- }
-
- $data = json_decode(wp_remote_retrieve_body($response), true);
-
- if ($data['status'] === 'OK' && !empty($data['results'])) {
- $result = $data['results'][0];
- $result = [
- 'lat' => $result['geometry']['location']['lat'],
- 'lng' => $result['geometry']['location']['lng'],
- 'formatted_address' => $result['formatted_address'],
- 'place_id' => $result['place_id'] ?? '',
- 'components' => $this->parseAddressComponents($result['address_components'] ?? [])
- ];
- $this->cache->set($key, $result);
- } else {
- $this->logError('Google Maps API error', [
- 'status' => $data['status'] ?? 'unknown',
- 'address' => $address
- ], 'warning');
- $result = null;
- }
-
- return $result;
- }
-
- /**
- * Reverse geocode coordinates to address
- */
- public function reverseGeocode(float $lat, float $lng): ?array
- {
- if (!$this->isSetUp()) {
- return null;
- }
-
- $params = [
- 'latlng' => "{$lat},{$lng}",
- 'key' => $this->private_key
- ];
-
- $key = $this->cache->generateKey(['reverse', $lat, $lng]);
- $cached = $this->cache->get($key);
- if ($cached) {
- return $cached;
- }
-
- $url = $this->apiBase . 'geocode/json?' . http_build_query($params);
- $response = wp_remote_get($url);
-
- if (is_wp_error($response)) {
- $this->logError('Reverse geocoding request failed', [
- 'error' => $response->get_error_message(),
- 'lat' => $lat,
- 'lng' => $lng
- ]);
- return null;
- }
-
- $data = json_decode(wp_remote_retrieve_body($response), true);
-
- if ($data['status'] === 'OK' && !empty($data['results'])) {
- $result = $data['results'][0];
- $return = [
- 'formatted_address' => $result['formatted_address'],
- 'place_id' => $result['place_id'] ?? '',
- 'components' => $this->parseAddressComponents($result['address_components'] ?? [])
- ];
- $this->cache->set($key, $return);
- return $return;
- }
-
- return null;
- }
-
- /**
- * Get place details by place ID
- */
- public function getPlaceDetails(string $place_id, array $fields = []): ?array
- {
- if (!$this->isSetUp()) {
- return null;
- }
-
- $default_fields = ['name', 'formatted_address', 'geometry', 'rating', 'formatted_phone_number'];
- $fields = empty($fields) ? $default_fields : $fields;
-
- $key = $this->cache->generateKey(['place', $place_id, $fields]);
- $cached = $this->cache->get($key);
- if ($cached) {
- return $cached;
- }
-
- $params = [
- 'place_id' => $place_id,
- 'fields' => implode(',', $fields),
- 'key' => $this->private_key
- ];
-
- $url = $this->apiBase . 'place/details/json?' . http_build_query($params);
- $response = wp_remote_get($url);
-
- if (is_wp_error($response)) {
- $this->logError('Place details request failed', [
- 'error' => $response->get_error_message(),
- 'place_id' => $place_id
- ]);
- return null;
- }
-
- $data = json_decode(wp_remote_retrieve_body($response), true);
-
- if ($data['status'] === 'OK' && !empty($data['result'])) {
- $result = $data['result'];
- $this->cache->set($key, $result);
- return $result;
- }
-
- return null;
- }
-
- /**
- * Search for places nearby
- */
- public function searchNearby(float $lat, float $lng, int $radius = 1000, string $type = ''): array
- {
- if (!$this->isSetUp()) {
- return [];
- }
-
- $params = [
- 'location' => "{$lat},{$lng}",
- 'radius' => $radius,
- 'key' => $this->private_key
- ];
-
- if (!empty($type)) {
- $params['type'] = $type;
- }
-
- $key = $this->cache->generateKey(['nearby', $lat, $lng, $radius, $type]);
- $cached = $this->cache->get($key);
- if ($cached) {
- return $cached;
- }
-
- $url = $this->apiBase . 'place/nearbysearch/json?' . http_build_query($params);
- $response = wp_remote_get($url);
-
- if (is_wp_error($response)) {
- $this->logError('Nearby search request failed', [
- 'error' => $response->get_error_message(),
- 'lat' => $lat,
- 'lng' => $lng,
- 'radius' => $radius
- ]);
- return [];
- }
-
- $data = json_decode(wp_remote_retrieve_body($response), true);
-
- if ($data['status'] === 'OK' && !empty($data['results'])) {
- $return = $data['results'];
- $this->cache->set($key, $return);
- return $return;
- }
-
- return [];
- }
-
- /**
- * Calculate distance between two points
- */
- public function calculateDistance(array $origin, array $destination, string $mode = 'driving'): ?array
- {
- if (!$this->isSetUp()) {
- return null;
- }
-
- // Handle different input formats
- $origin_str = is_array($origin) ? "{$origin['lat']},{$origin['lng']}" : $origin;
- $dest_str = is_array($destination) ? "{$destination['lat']},{$destination['lng']}" : $destination;
-
- $params = [
- 'origins' => $origin_str,
- 'destinations' => $dest_str,
- 'mode' => $mode,
- 'units' => 'metric',
- 'key' => $this->private_key
- ];
-
- $key = $this->cache->generateKey(['distance', $origin_str, $dest_str, $mode]);
- $cached = $this->cache->get($key);
- if ($cached) {
- return $cached;
- }
-
- $url = $this->apiBase . 'distancematrix/json?' . http_build_query($params);
- $response = wp_remote_get($url);
-
- if (is_wp_error($response)) {
- $this->logError('Distance calculation request failed', [
- 'error' => $response->get_error_message(),
- 'origin' => $origin_str,
- 'destination' => $dest_str
- ]);
- return null;
- }
-
- $data = json_decode(wp_remote_retrieve_body($response), true);
-
- if ($data['status'] === 'OK' && !empty($data['rows'][0]['elements'][0])) {
- $element = $data['rows'][0]['elements'][0];
- if ($element['status'] === 'OK') {
- $return = [
- 'distance' => $element['distance'],
- 'duration' => $element['duration'],
- 'mode' => $mode
- ];
- $this->cache->set($key, $return);
- return $return;
- }
- }
-
- return null;
- }
-
- /**
- * Get the JavaScript API URL with key
- */
- public function getJavaScriptApiUrl(array $libraries = ['places', 'marker', 'geometry']): string
- {
- if (empty($this->public_key)) {
- return '';
- }
-
- $params = [
- 'key' => $this->public_key,
- 'libraries' => implode(',', $libraries),
- 'callback' => 'initGoogleMaps',
- ];
-
- return $this->apiBase . 'js?' . http_build_query($params);
- }
-
- /**
- * Enqueue Google Maps script when needed
- */
- public function maybeEnqueueMapsScript(): void
- {
- // Only enqueue if we're on a page that needs maps
- if (!$this->shouldEnqueueMaps()) {
- return;
- }
-
- if (!$this->isSetUp()) {
- return;
- }
-
- $this->ensureInitialized();
-
// Register Google Maps API
wp_register_script(
'google-maps-api',
@@ -499,36 +134,69 @@
'strategy' => 'defer'
]
);
+ }
+ public function getJavaScriptApiUrl(array $libraries = ['places', 'marker', 'geometry']): string
+ {
+ if (empty($this->getPublicKey())) {
+ return '';
+ }
+
+ $params = [
+ 'key' => $this->getPublicKey(),
+ 'libraries' => implode(',', $libraries),
+ 'callback' => 'initGoogleMaps',
+ ];
+
+ return $this->apiBase . 'js?' . http_build_query($params);
+ }
+
+
+ public function maybeEnqueueMapsScript():void
+ {
+ if (!$this->isSetUp()) {
+ return;
+ }
+ if (!$this->shouldEnqueueMaps()) {
+ return;
+ }
// Add initialization script
- wp_add_inline_script('google-maps-api', '
- // Set map defaults
- window.jvbMapDefaults = {
- lat: ' . self::DEFAULT_LAT . ',
- lng: ' . self::DEFAULT_LNG . ',
- zoom: ' . self::DEFAULT_ZOOM . ',
- mapId: "' . esc_js($this->map_id) . '"
- };
+ wp_add_inline_script('google-maps-api',
+ sprintf(
+ '
+ // Set map defaults
+ window.jvbMapDefaults = {
+ lat: %s,
+ lng: %s,
+ zoom: %s,
+ mapId: "%s"
+ };
- // Google Maps initialization callback
- window.initGoogleMaps = function() {
- // Dispatch ready event
- document.dispatchEvent(new Event("googleMapsReady"));
+ // Google Maps initialization callback
+ window.initGoogleMaps = function() {
+ // Dispatch ready event
+ document.dispatchEvent(new Event("googleMapsReady"));
- // Call any registered callbacks
- if (window.googleMapsCallbacks && Array.isArray(window.googleMapsCallbacks)) {
- window.googleMapsCallbacks.forEach(function(callback) {
- if (typeof callback === "function") {
- callback();
+ // Call any registered callbacks
+ if (window.googleMapsCallbacks && Array.isArray(window.googleMapsCallbacks)) {
+ window.googleMapsCallbacks.forEach(function(callback) {
+ if (typeof callback === "function") {
+ callback();
+ }
+ });
+ window.googleMapsCallbacks = [];
}
- });
- window.googleMapsCallbacks = [];
- }
- };
+ };
- // Initialize callback queue
- window.googleMapsCallbacks = window.googleMapsCallbacks || [];
- ', 'before');
+ // Initialize callback queue
+ window.googleMapsCallbacks = window.googleMapsCallbacks || [];
+ ',
+ $this->getDefaultLat(),
+ $this->getDefaultLng(),
+ $this->getDefaultZoom(),
+ esc_js($this->getMapID())
+ ),
+ 'before');
// Enqueue scripts
wp_enqueue_script('google-maps-api');
@@ -541,134 +209,53 @@
'service' => $this->service_name
]);
}
-
- /**
- * Check if maps should be enqueued on current page
- */
- private function shouldEnqueueMaps(): bool
- {
- // Check for shortcodes
- if (is_singular() && get_post_meta(get_the_ID(), BASE . 'has_map', true) === true) {
- return true;
- }
-
- if (!empty(Registrar::withFeature('is_content', 'term')) && get_term_meta(get_queried_object_id(), BASE . 'has_map', true) === true) {
- return true;
- }
-
- // Check for specific page templates or post types
- if (is_singular(['shop', 'artist', 'location'])) {
- return true;
- }
-
- // Check for admin pages that need maps
- if (is_admin()) {
- $screen = get_current_screen();
- if ($screen && in_array($screen->post_type, ['shop', 'artist', 'location'])) {
+ protected function shouldEnqueueMaps():bool
+ {
+ //See if it's a singular post/page that has the map feature flagged
+ if (is_singular() && get_post_meta(get_the_ID(), BASE.'has_map', true) === true) {
return true;
}
+ if (is_tax() && get_term_meta(get_queried_object_id(), BASE . 'has_map', true) === true) {
+ return true;
+ }
+
+ // Allow filtering
+ return apply_filters('jvb_should_enqueue_maps', false);
}
- // Allow filtering
- return apply_filters('jvb_should_enqueue_maps', false);
- }
-
- /**
- * Parse address components from Google response
- */
- private function parseAddressComponents(array $components): array
+ public function getServiceDescription():string
{
- $parsed = [
- 'street_number' => '',
- 'street' => '',
- 'city' => '',
- 'province' => '',
- 'country' => '',
- 'postal_code' => ''
- ];
-
- foreach ($components as $component) {
- $types = $component['types'];
-
- if (in_array('street_number', $types)) {
- $parsed['street_number'] = $component['long_name'];
- }
- if (in_array('route', $types)) {
- $parsed['street'] = $component['long_name'];
- }
- if (in_array('locality', $types)) {
- $parsed['city'] = $component['long_name'];
- }
- if (in_array('administrative_area_level_1', $types)) {
- $parsed['province'] = $component['short_name'];
- }
- if (in_array('country', $types)) {
- $parsed['country'] = $component['short_name'];
- }
- if (in_array('postal_code', $types)) {
- $parsed['postal_code'] = $component['long_name'];
- }
- }
-
- // Combine street number and street name
- if ($parsed['street_number'] && $parsed['street']) {
- $parsed['street'] = $parsed['street_number'] . ' ' . $parsed['street'];
- }
-
- return $parsed;
+ return 'Allows for a map field, utilizing Google Maps';
}
-
- /**
- * Get Map ID
- */
- public function getMapId(): string
+ /**********************************************************************
+ * UTILITY
+ **********************************************************************/
+ protected function getPrivateKey():string|false
{
- return $this->map_id;
+ return $this->loadCredentials()['private_key']??false;
}
-
- /**
- * Get default map settings
- */
- public function getDefaultMapSettings(): array
+ protected function getPublicKey():string|false
{
- return [
- 'center' => [
- 'lat' => self::DEFAULT_LAT,
- 'lng' => self::DEFAULT_LNG
- ],
- 'zoom' => $this->credentials['default_zoom'] ?? self::DEFAULT_ZOOM,
- 'mapId' => $this->map_id,
- 'styles' => $this->getMapStyles()
- ];
+ return $this->loadCredentials()['public_key']??false;
}
- /**
- * Get map styles based on theme
- */
- private function getMapStyles(): array
+ protected function getMapID():string|false
{
- $theme = $this->credentials['theme'] ?? 'default';
-
- switch ($theme) {
- case 'dark':
- return [
- ['elementType' => 'geometry', 'stylers' => [['color' => '#242f3e']]],
- ['elementType' => 'labels.text.fill', 'stylers' => [['color' => '#746855']]],
- ['elementType' => 'labels.text.stroke', 'stylers' => [['color' => '#242f3e']]],
- ['featureType' => 'road', 'elementType' => 'geometry', 'stylers' => [['color' => '#38414e']]],
- ['featureType' => 'water', 'elementType' => 'geometry', 'stylers' => [['color' => '#17263c']]]
- ];
-
- case 'minimal':
- return [
- ['featureType' => 'poi', 'stylers' => [['visibility' => 'off']]],
- ['featureType' => 'transit', 'stylers' => [['visibility' => 'off']]]
- ];
-
- default:
- return [];
- }
+ return $this->loadCredentials()['map_id']??false;
}
+ protected function getDefaultZoom():string|false
+ {
+ return $this->loadCredentials()['default_zoom']??false;
+ }
+ protected function getDefaultLng():string|false
+ {
+ return $this->loadCredentials()['default_lng']??false;
+ }
+ protected function getDefaultLat():string|false
+ {
+ return $this->loadCredentials()['default_lat']??false;
+ }
+
/**
* Generate map application links
@@ -683,152 +270,108 @@
'waze' => "https://waze.com/ul?ll={$lat},{$lng}&navigate=yes"
];
}
-
- /**
- * Render a frontend display map
- */
- public function renderDisplayMap(array $location, array $options = []): string
+ private function parseAddressComponents(array $components):array
{
- if (!$this->isSetUp() || empty($location['lat']) || empty($location['lng'])) {
- return '';
- }
-
- $map_id = 'map-' . uniqid();
- $lat = (float)$location['lat'];
- $lng = (float)$location['lng'];
- $address = $location['address'] ?? '';
-
- $map_options = array_merge([
- 'height' => '250px',
- 'zoom' => 15,
- 'show_marker' => true,
- 'show_info_window' => true,
- 'info_content' => $address,
- 'interactive' => true
- ], $options);
-
- ob_start();
- ?>
- <div id="<?= esc_attr($map_id); ?>"
- class="jvb-location-map-display"
- style="height: <?= esc_attr($map_options['height']); ?>; border-radius: 8px; overflow: hidden;">
- </div>
-
- <script>
- document.addEventListener('DOMContentLoaded', function() {
- function initDisplayMap() {
- if (!window.jvbMaps || !window.jvbMaps.ready) {
- setTimeout(initDisplayMap, 100);
- return;
- }
-
- window.jvbMaps.createDisplayMap('<?= esc_js($map_id); ?>', {
- lat: <?= $lat; ?>,
- lng: <?= $lng; ?>,
- zoom: <?= intval($map_options['zoom']); ?>,
- show_marker: <?= $map_options['show_marker'] ? 'true' : 'false'; ?>,
- show_info_window: <?= $map_options['show_info_window'] ? 'true' : 'false'; ?>,
- info_content: <?= json_encode($map_options['info_content']); ?>,
- interactive: <?= $map_options['interactive'] ? 'true' : 'false'; ?>
- });
+ $parsed = [];
+ $check = [
+ 'street_number' => 'long_name',
+ 'street' => 'long_name',
+ 'city' => 'long_name',
+ 'province' => 'short_name',
+ 'country' => 'short_name',
+ 'postal_code' => 'long_name'
+ ];
+ foreach ($components as $c) {
+ $types = $c['types'];
+ foreach ($check as $ch => $index) {
+ if (in_array($ch, $types)) {
+ $parsed[$ch] = $c[$index];
}
-
- if (typeof google !== 'undefined' && google.maps) {
- initDisplayMap();
- } else {
- document.addEventListener('googleMapsReady', initDisplayMap);
- }
- });
- </script>
- <?php
-
- return ob_get_clean();
- }
-
- /**
- * Render map application links
- */
- public function renderMapLinks(float $lat, float $lng, ?string $address = null, array $options = []): string
- {
- $links = $this->generateMapLinks($lat, $lng, $address);
-
- $link_options = array_merge([
- 'show_icons' => true,
- 'style' => 'buttons', // 'buttons' or 'text'
- 'include' => ['google', 'apple'], // which links to show
- 'target' => '_blank'
- ], $options);
-
- $output = '<div class="jvb-map-links' . ($link_options['style'] === 'buttons' ? ' button-style' : ' text-style') . '">';
-
- foreach ($link_options['include'] as $provider) {
- if (!isset($links[$provider])) continue;
-
- $icon = '';
- $label = '';
-
- switch ($provider) {
- case 'google':
- $icon = $link_options['show_icons'] ? jvbIcon('google') : '';
- $label = 'Google Maps';
- break;
- case 'apple':
- $icon = $link_options['show_icons'] ? jvbIcon('apple') : '';
- $label = 'Apple Maps';
- break;
- case 'waze':
- $icon = $link_options['show_icons'] ? jvbIcon('waze') : '';
- $label = 'Waze';
- break;
}
-
- $output .= sprintf(
- '<a href="%s" target="%s" class="map-link %s-maps-link">%s<span>%s</span></a>',
- esc_url($links[$provider]),
- esc_attr($link_options['target']),
- esc_attr($provider),
- $icon,
- esc_html($label)
- );
}
-
- $output .= '</div>';
-
- return $output;
+ if (!empty($parsed['street_number']) && !empty($parsed['street'])) {
+ $parsed['street'] = $parsed['street_number'] . ' ' . $parsed['street'];
+ }
+ return $parsed;
}
- /**
- * Format address for display
- */
- public function formatAddress(array $location, string $format = 'full'): string
+ public function reverseGeocode(float $lat, float $lng):array|false
{
- if (!empty($location['address'])) {
- return $location['address'];
+ if (!$this->isSetUp()) {
+ return false;
+ }
+ $params = [
+ 'latlng' => "{$lat},{$lng}",
+ 'key' => $this->getPrivateKey()
+ ];
+ return $this->getRequest(
+ sprintf(
+ '%s/geocode/json',
+ $this->apiBase,
+ ),
+ $params
+ );
+ }
+ protected function handleGeocodeResponse(array $data, string $method):array
+ {
+ if (!array_key_exists('status', $data) || $data['status'] !== 'OK' || !array_key_exists('results', $data) || empty($data['results'])) {
+ return $this->response(false, 'Invalid response provided');
+ }
+ $result = $data['results'][0];
+ return $this->response(true, 'Successfully reversed geocode',[
+ 'lat' => $result['geometry']['location']['lat'],
+ 'lng' => $result['geometry']['location']['lng'],
+ 'formatted_address' => $result['formatted_address'],
+ 'place_id' => $result['place_id']??'',
+ 'components' => $this->parseAddressComponents($result['address_compenents']??[])
+ ]);
}
- // Build address from components if no formatted address
- $parts = [];
+ public function geocodeAddress(string $address):array|false
+ {
+ if (!$this->isSetUp()) {
+ return false;
+ }
+ $params = [
+ 'address' => $address,
+ 'key' => $this->getPrivateKey()
+ ];
+ return $this->getRequest(
+ sprintf(
+ '%s/geocode/json',
+ $this->apiBase
+ )
+ );
+ }
- switch ($format) {
- case 'full':
- if (!empty($location['street'])) $parts[] = $location['street'];
- if (!empty($location['city'])) $parts[] = $location['city'];
- if (!empty($location['province'])) $parts[] = $location['province'];
- if (!empty($location['postal_code'])) $parts[] = $location['postal_code'];
- if (!empty($location['country'])) $parts[] = $location['country'];
- break;
+ protected function getRequestHeaders(): array
+ {
+ return [
+ 'Accept' => 'application/json',
+ 'Content-Type' => 'application/json'
+ ];
+ }
- case 'compact':
- if (!empty($location['city']) && !empty($location['province'])) {
- $parts[] = $location['city'] . ', ' . $location['province'];
- }
- break;
+ protected function handleResponse(\WP_Error|array $response, string $method, string $endpoint): array
+ {
+ if (is_wp_error($response) && $response->get_error_message() === 'rate_limit_exceeded') {
+ return $this->response(false, 'Rate limit exceeded');
+ }
+ $endpoint = str_replace($this->apiBase, '', $endpoint);
+ return match ($endpoint) {
+ '/geocode/json' => $this->handleGeocodeResponse($response, $method),
+ default => $this->response(false, 'Unhandled response for '.$endpoint.' endpoint, with method: '.$method),
+ };
+ }
- case 'city_only':
- if (!empty($location['city'])) $parts[] = $location['city'];
- break;
+ protected function performConnectionTest():bool
+ {
+ if (!$this->isSetUp()) {
+ return false;
}
- return implode(', ', $parts);
+ $test = 'Edmonton, Alberta, Canada';
+ $result = $this->geocodeAddress($test);
+ return $result['success'];
}
}
--
Gitblit v1.10.0