文章目录
-
- 在当今数字化时代,网站的功能丰富性直接关系到用户体验和网站价值。对于许多类型的WordPress网站来说,集成天气和地图功能可以显著提升实用性和用户粘性。旅游博客需要展示目的地天气,本地商家网站需要显示店铺位置,活动策划网站需要提供场地地图和天气信息——这些场景都说明了集成这些功能的必要性。 传统的解决方案是使用第三方小工具或iframe嵌入,但这些方法往往存在加载速度慢、样式不统一、功能受限等问题。通过WordPress代码二次开发实现这些功能,不仅可以获得更好的性能和控制权,还能确保与网站主题完美融合,提供一致的用户体验。 本教程将引导您完成在WordPress中集成天气与地图显示功能的完整过程,从API选择到代码实现,再到前端展示,为您提供一个完整的解决方案。
-
- 在开始编码之前,我们需要选择合适的数据源。对于天气数据,有几个流行的API可供选择: OpenWeatherMap:提供免费层级的API调用,包含当前天气、预报和历史数据 WeatherAPI:简单易用,免费套餐包含300万次调用/月 AccuWeather:数据准确但免费层级限制较多 对于地图功能,主流选择包括: Google Maps API:功能强大但需要绑定信用卡(有免费额度) Mapbox:提供美观的地图样式和灵活的定制选项 Leaflet + OpenStreetMap:完全开源免费的解决方案 考虑到成本和易用性,本教程将使用: 天气数据:OpenWeatherMap API(免费版) 地图显示:Leaflet + OpenStreetMap(完全免费)
- OpenWeatherMap注册步骤: 访问 OpenWeatherMap官网 点击"Sign Up"创建账户 登录后进入API Keys页面 生成新的API密钥并保存 Leaflet无需API密钥,可以直接使用。
- 确保您具备以下环境: 本地或线上的WordPress安装(建议5.0以上版本) 代码编辑器(VS Code、Sublime Text等) FTP客户端或文件管理器(用于上传代码) 基本的PHP、JavaScript和HTML/CSS知识
- 为了避免主题更新导致代码丢失,我们将创建一个独立插件: 在WordPress的wp-content/plugins/目录下创建新文件夹weather-map-integration 在该文件夹中创建主插件文件weather-map-integration.php
-
- 打开weather-map-integration.php,添加以下插件声明: <?php /** * Plugin Name: Weather & Map Integration * Plugin URI: https://yourwebsite.com/ * Description: 在WordPress中集成天气与地图显示功能 * Version: 1.0.0 * Author: 您的名称 * Author URI: https://yourwebsite.com/ * License: GPL v2 or later * Text Domain: weather-map-integration */ // 防止直接访问 if (!defined('ABSPATH')) { exit; }
- 在插件头部信息后添加以下常量定义: // 定义插件路径和URL常量 define('WMI_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('WMI_PLUGIN_URL', plugin_dir_url(__FILE__)); // 定义API密钥常量(在实际使用中应从设置页面获取) define('WMI_OPENWEATHER_API_KEY', 'your_openweather_api_key_here'); // 插件版本 define('WMI_VERSION', '1.0.0');
- 使用面向对象的方式组织插件代码: class Weather_Map_Integration { private static $instance = null; public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this->init_hooks(); } private function init_hooks() { // 初始化钩子 add_action('init', array($this, 'init')); add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_init', array($this, 'register_settings')); add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); // 注册短代码 add_shortcode('weather_display', array($this, 'weather_shortcode')); add_shortcode('map_display', array($this, 'map_shortcode')); add_shortcode('weather_map_combo', array($this, 'weather_map_combo_shortcode')); } public function init() { // 初始化代码 load_plugin_textdomain('weather-map-integration', false, dirname(plugin_basename(__FILE__)) . '/languages'); } // 其他方法将在后续部分添加 } // 初始化插件 Weather_Map_Integration::get_instance();
-
- 在插件类中添加以下方法: public function add_admin_menu() { add_menu_page( '天气与地图设置', '天气地图', 'manage_options', 'weather-map-settings', array($this, 'settings_page'), 'dashicons-location-alt', 80 ); add_submenu_page( 'weather-map-settings', 'API设置', 'API设置', 'manage_options', 'weather-map-api-settings', array($this, 'api_settings_page') ); add_submenu_page( 'weather-map-settings', '显示设置', '显示设置', 'manage_options', 'weather-map-display-settings', array($this, 'display_settings_page') ); }
- public function register_settings() { // API设置 register_setting('wmi_api_settings', 'wmi_openweather_api_key'); register_setting('wmi_api_settings', 'wmi_default_city'); register_setting('wmi_api_settings', 'wmi_default_country'); // 显示设置 register_setting('wmi_display_settings', 'wmi_temperature_unit'); register_setting('wmi_display_settings', 'wmi_map_height'); register_setting('wmi_display_settings', 'wmi_map_width'); register_setting('wmi_display_settings', 'wmi_default_zoom'); register_setting('wmi_display_settings', 'wmi_show_attribution'); // API设置部分 add_settings_section( 'wmi_api_section', 'API配置', array($this, 'api_section_callback'), 'weather-map-api-settings' ); add_settings_field( 'wmi_openweather_api_key', 'OpenWeatherMap API密钥', array($this, 'api_key_field_callback'), 'weather-map-api-settings', 'wmi_api_section' ); add_settings_field( 'wmi_default_city', '默认城市', array($this, 'default_city_field_callback'), 'weather-map-api-settings', 'wmi_api_section' ); // 显示设置部分 add_settings_section( 'wmi_display_section', '显示设置', array($this, 'display_section_callback'), 'weather-map-display-settings' ); add_settings_field( 'wmi_temperature_unit', '温度单位', array($this, 'temperature_unit_field_callback'), 'weather-map-display-settings', 'wmi_display_section' ); // 更多设置字段... } public function api_section_callback() { echo '<p>配置天气和地图API的相关设置。请确保您已注册相应的API服务。</p>'; } public function api_key_field_callback() { $api_key = get_option('wmi_openweather_api_key', ''); echo '<input type="text" id="wmi_openweather_api_key" name="wmi_openweather_api_key" value="' . esc_attr($api_key) . '" class="regular-text" />'; echo '<p class="description">请输入您的OpenWeatherMap API密钥。如果没有,请访问<a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap官网</a>注册获取。</p>'; } // 其他字段回调函数...
- public function api_settings_page() { if (!current_user_can('manage_options')) { return; } ?> <div class="wrap"> <h1><?php echo esc_html(get_admin_page_title()); ?></h1> <form action="options.php" method="post"> <?php settings_fields('wmi_api_settings'); do_settings_sections('weather-map-api-settings'); submit_button('保存设置'); ?> </form> </div> <?php } public function display_settings_page() { if (!current_user_can('manage_options')) { return; } ?> <div class="wrap"> <h1><?php echo esc_html(get_admin_page_title()); ?></h1> <form action="options.php" method="post"> <?php settings_fields('wmi_display_settings'); do_settings_sections('weather-map-display-settings'); submit_button('保存设置'); ?> </form> </div> <?php }
-
- 在插件目录下创建includes/class-weather-data.php: <?php if (!defined('ABSPATH')) { exit; } class WMI_Weather_Data { private $api_key; private $base_url = 'https://api.openweathermap.org/data/2.5/'; public function __construct($api_key = '') { $this->api_key = $api_key ?: get_option('wmi_openweather_api_key', ''); } /** * 获取当前天气数据 */ public function get_current_weather($city, $country = '', $units = 'metric') { $location = $city; if (!empty($country)) { $location .= ',' . $country; } $transient_key = 'wmi_current_weather_' . md5($location . $units); $cached_data = get_transient($transient_key); if ($cached_data !== false) { return $cached_data; } $url = $this->base_url . 'weather'; $args = array( 'q' => $location, 'appid' => $this->api_key, 'units' => $units, 'lang' => $this->get_language_code() ); $response = wp_remote_get(add_query_arg($args, $url)); if (is_wp_error($response)) { return array('error' => $response->get_error_message()); } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['cod']) && $data['cod'] != 200) { return array('error' => $data['message'] ?? '未知错误'); } // 缓存数据10分钟 set_transient($transient_key, $data, 10 * MINUTE_IN_SECONDS); return $data; } /** * 获取天气预报数据 */ public function get_forecast($city, $country = '', $units = 'metric', $days = 5) { $location = $city; if (!empty($country)) { $location .= ',' . $country; } $transient_key = 'wmi_forecast_' . md5($location . $units . $days); $cached_data = get_transient($transient_key); if ($cached_data !== false) { return $cached_data; } $url = $this->base_url . 'forecast'; $args = array( 'q' => $location, 'appid' => $this->api_key, 'units' => $units, 'cnt' => $days * 8, // 每3小时一个数据点 'lang' => $this->get_language_code() ); $response = wp_remote_get(add_query_arg($args, $url)); if (is_wp_error($response)) { return array('error' => $response->get_error_message()); } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['cod']) && $data['cod'] != 200) { return array('error' => $data['message'] ?? '未知错误'); } // 缓存数据30分钟 set_transient($transient_key, $data, 30 * MINUTE_IN_SECONDS); return $data; } /** * 根据IP获取位置信息 */ public function get_location_by_ip() { $transient_key = 'wmi_location_ip_' . $_SERVER['REMOTE_ADDR']; $cached_data = get_transient($transient_key); if ($cached_data !== false) { return $cached_data; } // 使用ipapi.co服务(免费) $response = wp_remote_get('https://ipapi.co/json/'); if (is_wp_error($response)) { return array('city' => 'Beijing', 'country' => 'CN'); } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); $location = array( 'city' => $data['city'] ?? 'Beijing', 'country' => $data['country'] ?? 'CN' ); // 缓存24小时 set_transient($transient_key, $location, 24 * HOUR_IN_SECONDS); return $location; } /** * 获取语言代码 */ private function get_language_code() { $locale = get_locale(); $lang_map = array( 'zh_CN' => 'zh_cn', 'zh_TW' => 'zh_tw', 'en_US' => 'en', 'en_GB' => 'en', 'es_ES' => 'es', 'fr_FR' => 'fr', 'de_DE' => 'de', 'ja' => 'ja', 'ko_KR' => 'kr' ); return $lang_map[$locale] ?? 'en'; } /** * 处理天气数据为前端可用格式 */ public function process_weather_data($weather_data) { if (isset($weather_data['error'])) { return $weather_data; } $processed = array( 'location' => $weather_data['name'] . ', ' . ($weather_data['sys']['country'] ?? ''), 'temperature' => round($weather_data['main']['temp']), 'feels_like' => round($weather_data['main']['feels_like']), 'description' => $weather_data['weather'][0]['description'], 'icon' => $weather_data['weather'][0]['icon'], 'humidity' => $weather_data['main']['humidity'], 'pressure' => $weather_data['main']['pressure'], 'wind_speed' => $weather_data['wind']['speed'], 'wind_deg' => $weather_data['wind']['deg'], 'sunrise' => date('H:i', $weather_data['sys']['sunrise']), 'sunset' => date('H:i', $weather_data['sys']['sunset']), 'timestamp' => time() ); return $processed; } }
- 在主插件类中添加天气短代码方法: public function weather_shortcode($atts) { $atts = shortcode_atts(array( 'city' => '', 'country' => '', 'units' => get_option('wmi_temperature_unit', 'metric'), 'show_forecast' => 'false', 'forecast_days' => 3, 'layout' => 'compact', // compact, detailed, card 'title' => '当前天气' ), $atts, 'weather_display'); // 如果没有指定城市,尝试根据IP获取 if (empty($atts['city'])) { $weather_data = new WMI_Weather_Data(); $location = $weather_data->get_location_by_ip(); $atts['city'] = $location['city']; $atts['country'] = $location['country']; } // 获取天气数据 $weather_api = new WMI_Weather_Data(); $current_weather = $weather_api->get_current_weather($atts['city'], $atts['country'], $atts['units']); if (isset($current_weather['error'])) { return '<div class="wmi-error">无法获取天气数据: ' . esc_html($current_weather['error']) . '</div>'; } $processed_weather = $weather_api->process_weather_data($current_weather); // 获取天气预报(如果需要) $forecast_data = array(); if ($atts['show_forecast'] === 'true') { $forecast = $weather_api->get_forecast($atts['city'], $atts['country'], $atts['units'], $atts['forecast_days']); if (!isset($forecast['error'])) { $forecast_data = $this->process_forecast_data($forecast); } } // 生成输出HTML ob_start(); ?> <div class="wmi-weather-container wmi-layout-<?php echo esc_attr($atts['layout']); ?>" data-city="<?php echo esc_attr($atts['city']); ?>" data-country="<?php echo esc_attr($atts['country']); ?>" data-units="<?php echo esc_attr($atts['units']); ?>"> <div class="wmi-weather-header"> <h3 class="wmi-weather-title"><?php echo esc_html($atts['title']); ?></h3> <div class="wmi-location"><?php echo esc_html($processed_weather['location']); ?></div> </div> <div class="wmi-current-weather"> <div class="wmi-weather-main"> <div class="wmi-temperature"> <span class="wmi-temp-value"><?php echo esc_html($processed_weather['temperature']); ?></span> <span class="wmi-temp-unit">°<?php echo $atts['units'] === 'metric' ? 'C' : 'F'; ?></span> </div> <div class="wmi-weather-icon"> <img src="https://openweathermap.org/img/wn/<?php echo esc_attr($processed_weather['icon']); ?>@2x.png" alt="<?php echo esc_attr($processed_weather['description']); ?>"> </div> </div> <div class="wmi-weather-details"> <div class="wmi-weather-desc"><?php echo esc_html($processed_weather['description']); ?></div> <div class="wmi-weather-meta"> <span class="wmi-feels-like">体感温度: <?php echo esc_html($processed_weather['feels_like']); ?>°</span> <span class="wmi-humidity">湿度: <?php echo esc_html($processed_weather['humidity']); ?>%</span> <span class="wmi-wind">风速: <?php echo esc_html($processed_weather['wind_speed']); ?> m/s</span> </div> </div> </div> <?php if (!empty($forecast_data)): ?> <div class="wmi-forecast"> <h4 class="wmi-forecast-title"><?php echo esc_html($atts['forecast_days']); ?>天预报</h4> <div class="wmi-forecast-days"> <?php foreach ($forecast_data as $day): ?> <div class="wmi-forecast-day"> <div class="wmi-forecast-date"><?php echo esc_html($day['date']); ?></div> <div class="wmi-forecast-icon"> <img src="https://openweathermap.org/img/wn/<?php echo esc_attr($day['icon']); ?>.png" alt="<?php echo esc_attr($day['description']); ?>"> </div> <div class="wmi-forecast-temp"> <span class="wmi-forecast-temp-max"><?php echo esc_html($day['temp_max']); ?>°</span> <span class="wmi-forecast-temp-min"><?php echo esc_html($day['temp_min']); ?>°</span> </div> </div> <?php endforeach; ?> </div> </div> <?php endif; ?> <div class="wmi-weather-footer"> <div class="wmi-sun-times"> <span class="wmi-sunrise">日出: <?php echo esc_html($processed_weather['sunrise']); ?></span> <span class="wmi-sunset">日落: <?php echo esc_html($processed_weather['sunset']); ?></span> </div> <div class="wmi-update-time"> 更新时间: <?php echo date('H:i', $processed_weather['timestamp']); ?> </div> </div> </div> <?php return ob_get_clean(); } private function process_forecast_data($forecast_data) { $daily_data = array(); $grouped_by_day = array(); // 按日期分组 foreach ($forecast_data['list'] as $item) { $date = date('Y-m-d', $item['dt']); if (!isset($grouped_by_day[$date])) { $grouped_by_day[$date] = array(); } $grouped_by_day[$date][] = $item; } // 处理每天的数据 $count = 0; foreach ($grouped_by_day as $date => $day_items) { if ($count >= 5) break; // 最多显示5天 $temps = array(); $icons = array(); $descriptions = array(); foreach ($day_items as $item) { $temps[] = $item['main']['temp']; $icons[] = $item['weather'][0]['icon']; $descriptions[] = $item['weather'][0]['description']; } // 获取最常见的图标和描述 $icon_counts = array_count_values($icons); arsort($icon_counts); $most_common_icon = key($icon_counts); $desc_counts = array_count_values($descriptions); arsort($desc_counts); $most_common_desc = key($desc_counts); $daily_data[] = array( 'date' => date('m/d', strtotime($date)), 'day' => date('D', strtotime($date)), 'temp_max' => round(max($temps)), 'temp_min' => round(min($temps)), 'icon' => $most_common_icon, 'description' => $most_common_desc ); $count++; } return $daily_data; } ## 第五部分:地图功能实现 ### 5.1 创建地图数据类 在插件目录下创建`includes/class-map-data.php`: <?phpif (!defined('ABSPATH')) { exit; } class WMI_Map_Data { /** * 获取位置坐标 */ public function get_coordinates($location) { $transient_key = 'wmi_coordinates_' . md5($location); $cached_data = get_transient($transient_key); if ($cached_data !== false) { return $cached_data; } // 使用Nominatim(OpenStreetMap的搜索服务) $url = 'https://nominatim.openstreetmap.org/search'; $args = array( 'q' => $location, 'format' => 'json', 'limit' => 1, 'addressdetails' => 1 ); $response = wp_remote_get(add_query_arg($args, $url), array( 'headers' => array( 'User-Agent' => 'WordPress Weather Map Integration/1.0' ) )); if (is_wp_error($response)) { return array('error' => $response->get_error_message()); } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (empty($data)) { return array('error' => '未找到位置信息'); } $coordinates = array( 'lat' => $data[0]['lat'], 'lon' => $data[0]['lon'], 'display_name' => $data[0]['display_name'], 'address' => $data[0]['address'] ); // 缓存30天 set_transient($transient_key, $coordinates, 30 * DAY_IN_SECONDS); return $coordinates; } /** * 获取多个标记点 */ public function get_multiple_markers($locations) { $markers = array(); foreach ($locations as $location) { if (is_array($location) && isset($location['lat'], $location['lng'])) { $markers[] = array( 'lat' => $location['lat'], 'lng' => $location['lng'], 'title' => $location['title'] ?? '', 'description' => $location['description'] ?? '' ); } else { $coords = $this->get_coordinates($location); if (!isset($coords['error'])) { $markers[] = array( 'lat' => $coords['lat'], 'lng' => $coords['lon'], 'title' => is_array($location) ? ($location['title'] ?? '') : $location, 'description' => $coords['display_name'] ); } } } return $markers; } /** * 计算地图边界 */ public function calculate_bounds($markers) { if (empty($markers)) { return array( 'south' => 0, 'west' => 0, 'north' => 0, 'east' => 0 ); } $lats = array_column($markers, 'lat'); $lngs = array_column($markers, 'lng'); return array( 'south' => min($lats), 'west' => min($lngs), 'north' => max($lats), 'east' => max($lngs) ); } } ### 5.2 创建地图显示短代码 在主插件类中添加地图短代码方法: public function map_shortcode($atts) { $atts = shortcode_atts(array( 'location' => '', 'lat' => '', 'lng' => '', 'zoom' => get_option('wmi_default_zoom', 13), 'height' => get_option('wmi_map_height', '400px'), 'width' => get_option('wmi_map_width', '100%'), 'markers' => '', // JSON格式或逗号分隔 'show_search' => 'false', 'show_controls' => 'true', 'map_style' => 'streets', // streets, satellite, terrain 'title' => '位置地图' ), $atts, 'map_display'); // 生成唯一ID $map_id = 'wmi-map-' . uniqid(); // 处理位置数据 $locations = array(); if (!empty($atts['markers'])) { // 尝试解析JSON $markers_json = json_decode($atts['markers'], true); if (json_last_error() === JSON_ERROR_NONE) { $locations = $markers_json; } else { // 逗号分隔的字符串 $marker_list = explode(',', $atts['markers']); foreach ($marker_list as $marker) { $locations[] = trim($marker); } } } elseif (!empty($atts['location'])) { $locations[] = $atts['location']; } elseif (!empty($atts['lat']) && !empty($atts['lng'])) { $locations[] = array( 'lat' => $atts['lat'], 'lng' => $atts['lng'], 'title' => $atts['title'] ); } else { // 默认位置 $weather_data = new WMI_Weather_Data(); $default_location = $weather_data->get_location_by_ip(); $locations[] = $default_location['city'] . ', ' . $default_location['country']; } // 获取坐标 $map_api = new WMI_Map_Data(); $markers = $map_api->get_multiple_markers($locations); // 计算地图边界 $bounds = $map_api->calculate_bounds($markers); ob_start(); ?> <div class="wmi-map-container" id="<?php echo esc_attr($map_id); ?>-container"> <?php if (!empty($atts['title'])): ?> <h3 class="wmi-map-title"><?php echo esc_html($atts['title']); ?></h3> <?php endif; ?> <?php if ($atts['show_search'] === 'true'): ?> <div class="wmi-map-search"> <input type="text" id="<?php echo esc_attr($map_id); ?>-search" placeholder="搜索地点..." class="wmi-map-search-input"> <button type="button" class="wmi-map-search-button">搜索</button> </div> <?php endif; ?> <div class="wmi-map" id="<?php echo esc_attr($map_id); ?>" style="height: <?php echo esc_attr($atts['height']); ?>; width: <?php echo esc_attr($atts['width']); ?>;"> </div> <div class="wmi-map-info"> <?php if (!empty($markers) && count($markers) === 1): ?> <div class="wmi-location-info"> <strong><?php echo esc_html($markers[0]['title'] ?: '位置'); ?>:</strong> <span><?php echo esc_html($markers[0]['description']); ?></span> </div> <?php endif; ?> </div> </div> <script type="text/javascript"> document.addEventListener('DOMContentLoaded', function() { if (typeof L !== 'undefined') { initWmiMap('<?php echo esc_js($map_id); ?>', { markers: <?php echo json_encode($markers); ?>, bounds: <?php echo json_encode($bounds); ?>, zoom: <?php echo intval($atts['zoom']); ?>, showControls: <?php echo $atts['show_controls'] === 'true' ? 'true' : 'false'; ?>, mapStyle: '<?php echo esc_js($atts['map_style']); ?>' }); } }); </script> <?php return ob_get_clean(); } ### 5.3 创建组合短代码 public function weather_map_combo_shortcode($atts) { $atts = shortcode_atts(array( 'location' => '', 'city' => '', 'country' => '', 'layout' => 'side-by-side', // side-by-side, map-above, weather-above 'height' => '500px', 'show_forecast' => 'true', 'show_search' => 'true', 'title' => '天气与位置' ), $atts, 'weather_map_combo'); // 确定位置 if (!empty($atts['location'])) { $location_parts = explode(',', $atts['location']); $atts['city'] = trim($location_parts[0]); if (isset($location_parts[1])) { $atts['country'] = trim($location_parts[1]); } } ob_start(); ?> <div class="wmi-combo-container wmi-layout-<?php echo esc_attr($atts['layout']); ?>"> <h2 class="wmi-combo-title"><?php echo esc_html($atts['title']); ?></h2> <div class="wmi-combo-content"> <div class="wmi-combo-weather"> <?php echo $this->weather_shortcode(array( 'city' => $atts['city'], 'country' => $atts['country'], 'show_forecast' => $atts['show_forecast'], 'layout' => 'detailed', 'title' => '' )); ?> </div> <div class="wmi-combo-map"> <?php $location_str = !empty($atts['country']) ? $atts['city'] . ', ' . $atts['country'] : $atts['city']; echo $this->map_shortcode(array( 'location' => $location_str, 'height' => $atts['height'], 'show_search' => $atts['show_search'], 'title' => '' )); ?> </div> </div> </div> <?php return ob_get_clean(); } ## 第六部分:前端资源加载与样式 ### 6.1 注册和加载脚本样式 在主插件类中添加以下方法: public function enqueue_frontend_scripts() { // 加载Leaflet地图库 wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css', array(), '1.7.1'); wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', array(), '1.7.1', true); // 加载插件样式 wp_enqueue_style('wmi-frontend-style', WMI_PLUGIN_URL . 'assets/css/frontend.css', array(), WMI_VERSION); // 加载插件脚本 wp_enqueue_script('wmi-frontend-script', WMI_PLUGIN_URL . 'assets/js/frontend.js', array('jquery', 'leaflet-js'), WMI_VERSION, true); // 本地化脚本 wp_localize_script('wmi-frontend-script', 'wmi_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wmi_ajax_nonce'), 'default_location' => $this->get_default_location(), 'strings' => array( 'loading' => __('加载中...', 'weather-map-integration'), 'error' => __('发生错误', 'weather-map-integration'), 'search_placeholder' => __('输入地点
在当今数字化时代,网站的功能丰富性直接关系到用户体验和网站价值。对于许多类型的WordPress网站来说,集成天气和地图功能可以显著提升实用性和用户粘性。旅游博客需要展示目的地天气,本地商家网站需要显示店铺位置,活动策划网站需要提供场地地图和天气信息——这些场景都说明了集成这些功能的必要性。
传统的解决方案是使用第三方小工具或iframe嵌入,但这些方法往往存在加载速度慢、样式不统一、功能受限等问题。通过WordPress代码二次开发实现这些功能,不仅可以获得更好的性能和控制权,还能确保与网站主题完美融合,提供一致的用户体验。
本教程将引导您完成在WordPress中集成天气与地图显示功能的完整过程,从API选择到代码实现,再到前端展示,为您提供一个完整的解决方案。
在开始编码之前,我们需要选择合适的数据源。对于天气数据,有几个流行的API可供选择:
- OpenWeatherMap:提供免费层级的API调用,包含当前天气、预报和历史数据
- WeatherAPI:简单易用,免费套餐包含300万次调用/月
- AccuWeather:数据准确但免费层级限制较多
对于地图功能,主流选择包括:
- Google Maps API:功能强大但需要绑定信用卡(有免费额度)
- Mapbox:提供美观的地图样式和灵活的定制选项
- Leaflet + OpenStreetMap:完全开源免费的解决方案
考虑到成本和易用性,本教程将使用:
- 天气数据:OpenWeatherMap API(免费版)
- 地图显示:Leaflet + OpenStreetMap(完全免费)
OpenWeatherMap注册步骤:
- 访问 OpenWeatherMap官网
- 点击"Sign Up"创建账户
- 登录后进入API Keys页面
- 生成新的API密钥并保存
Leaflet无需API密钥,可以直接使用。
确保您具备以下环境:
- 本地或线上的WordPress安装(建议5.0以上版本)
- 代码编辑器(VS Code、Sublime Text等)
- FTP客户端或文件管理器(用于上传代码)
- 基本的PHP、JavaScript和HTML/CSS知识
为了避免主题更新导致代码丢失,我们将创建一个独立插件:
- 在WordPress的
wp-content/plugins/目录下创建新文件夹weather-map-integration - 在该文件夹中创建主插件文件
weather-map-integration.php
打开weather-map-integration.php,添加以下插件声明:
<?php
/**
* Plugin Name: Weather & Map Integration
* Plugin URI: https://yourwebsite.com/
* Description: 在WordPress中集成天气与地图显示功能
* Version: 1.0.0
* Author: 您的名称
* Author URI: https://yourwebsite.com/
* License: GPL v2 or later
* Text Domain: weather-map-integration
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
在插件头部信息后添加以下常量定义:
// 定义插件路径和URL常量
define('WMI_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WMI_PLUGIN_URL', plugin_dir_url(__FILE__));
// 定义API密钥常量(在实际使用中应从设置页面获取)
define('WMI_OPENWEATHER_API_KEY', 'your_openweather_api_key_here');
// 插件版本
define('WMI_VERSION', '1.0.0');
使用面向对象的方式组织插件代码:
class Weather_Map_Integration {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 初始化钩子
add_action('init', array($this, 'init'));
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
// 注册短代码
add_shortcode('weather_display', array($this, 'weather_shortcode'));
add_shortcode('map_display', array($this, 'map_shortcode'));
add_shortcode('weather_map_combo', array($this, 'weather_map_combo_shortcode'));
}
public function init() {
// 初始化代码
load_plugin_textdomain('weather-map-integration', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
// 其他方法将在后续部分添加
}
// 初始化插件
Weather_Map_Integration::get_instance();
在插件类中添加以下方法:
public function add_admin_menu() {
add_menu_page(
'天气与地图设置',
'天气地图',
'manage_options',
'weather-map-settings',
array($this, 'settings_page'),
'dashicons-location-alt',
80
);
add_submenu_page(
'weather-map-settings',
'API设置',
'API设置',
'manage_options',
'weather-map-api-settings',
array($this, 'api_settings_page')
);
add_submenu_page(
'weather-map-settings',
'显示设置',
'显示设置',
'manage_options',
'weather-map-display-settings',
array($this, 'display_settings_page')
);
}
public function register_settings() {
// API设置
register_setting('wmi_api_settings', 'wmi_openweather_api_key');
register_setting('wmi_api_settings', 'wmi_default_city');
register_setting('wmi_api_settings', 'wmi_default_country');
// 显示设置
register_setting('wmi_display_settings', 'wmi_temperature_unit');
register_setting('wmi_display_settings', 'wmi_map_height');
register_setting('wmi_display_settings', 'wmi_map_width');
register_setting('wmi_display_settings', 'wmi_default_zoom');
register_setting('wmi_display_settings', 'wmi_show_attribution');
// API设置部分
add_settings_section(
'wmi_api_section',
'API配置',
array($this, 'api_section_callback'),
'weather-map-api-settings'
);
add_settings_field(
'wmi_openweather_api_key',
'OpenWeatherMap API密钥',
array($this, 'api_key_field_callback'),
'weather-map-api-settings',
'wmi_api_section'
);
add_settings_field(
'wmi_default_city',
'默认城市',
array($this, 'default_city_field_callback'),
'weather-map-api-settings',
'wmi_api_section'
);
// 显示设置部分
add_settings_section(
'wmi_display_section',
'显示设置',
array($this, 'display_section_callback'),
'weather-map-display-settings'
);
add_settings_field(
'wmi_temperature_unit',
'温度单位',
array($this, 'temperature_unit_field_callback'),
'weather-map-display-settings',
'wmi_display_section'
);
// 更多设置字段...
}
public function api_section_callback() {
echo '<p>配置天气和地图API的相关设置。请确保您已注册相应的API服务。</p>';
}
public function api_key_field_callback() {
$api_key = get_option('wmi_openweather_api_key', '');
echo '<input type="text" id="wmi_openweather_api_key" name="wmi_openweather_api_key" value="' . esc_attr($api_key) . '" class="regular-text" />';
echo '<p class="description">请输入您的OpenWeatherMap API密钥。如果没有,请访问<a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap官网</a>注册获取。</p>';
}
// 其他字段回调函数...
public function register_settings() {
// API设置
register_setting('wmi_api_settings', 'wmi_openweather_api_key');
register_setting('wmi_api_settings', 'wmi_default_city');
register_setting('wmi_api_settings', 'wmi_default_country');
// 显示设置
register_setting('wmi_display_settings', 'wmi_temperature_unit');
register_setting('wmi_display_settings', 'wmi_map_height');
register_setting('wmi_display_settings', 'wmi_map_width');
register_setting('wmi_display_settings', 'wmi_default_zoom');
register_setting('wmi_display_settings', 'wmi_show_attribution');
// API设置部分
add_settings_section(
'wmi_api_section',
'API配置',
array($this, 'api_section_callback'),
'weather-map-api-settings'
);
add_settings_field(
'wmi_openweather_api_key',
'OpenWeatherMap API密钥',
array($this, 'api_key_field_callback'),
'weather-map-api-settings',
'wmi_api_section'
);
add_settings_field(
'wmi_default_city',
'默认城市',
array($this, 'default_city_field_callback'),
'weather-map-api-settings',
'wmi_api_section'
);
// 显示设置部分
add_settings_section(
'wmi_display_section',
'显示设置',
array($this, 'display_section_callback'),
'weather-map-display-settings'
);
add_settings_field(
'wmi_temperature_unit',
'温度单位',
array($this, 'temperature_unit_field_callback'),
'weather-map-display-settings',
'wmi_display_section'
);
// 更多设置字段...
}
public function api_section_callback() {
echo '<p>配置天气和地图API的相关设置。请确保您已注册相应的API服务。</p>';
}
public function api_key_field_callback() {
$api_key = get_option('wmi_openweather_api_key', '');
echo '<input type="text" id="wmi_openweather_api_key" name="wmi_openweather_api_key" value="' . esc_attr($api_key) . '" class="regular-text" />';
echo '<p class="description">请输入您的OpenWeatherMap API密钥。如果没有,请访问<a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap官网</a>注册获取。</p>';
}
// 其他字段回调函数...
public function api_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('wmi_api_settings');
do_settings_sections('weather-map-api-settings');
submit_button('保存设置');
?>
</form>
</div>
<?php
}
public function display_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('wmi_display_settings');
do_settings_sections('weather-map-display-settings');
submit_button('保存设置');
?>
</form>
</div>
<?php
}
public function api_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('wmi_api_settings');
do_settings_sections('weather-map-api-settings');
submit_button('保存设置');
?>
</form>
</div>
<?php
}
public function display_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('wmi_display_settings');
do_settings_sections('weather-map-display-settings');
submit_button('保存设置');
?>
</form>
</div>
<?php
}
在插件目录下创建includes/class-weather-data.php:
<?php
if (!defined('ABSPATH')) {
exit;
}
class WMI_Weather_Data {
private $api_key;
private $base_url = 'https://api.openweathermap.org/data/2.5/';
public function __construct($api_key = '') {
$this->api_key = $api_key ?: get_option('wmi_openweather_api_key', '');
}
/**
* 获取当前天气数据
*/
public function get_current_weather($city, $country = '', $units = 'metric') {
$location = $city;
if (!empty($country)) {
$location .= ',' . $country;
}
$transient_key = 'wmi_current_weather_' . md5($location . $units);
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
$url = $this->base_url . 'weather';
$args = array(
'q' => $location,
'appid' => $this->api_key,
'units' => $units,
'lang' => $this->get_language_code()
);
$response = wp_remote_get(add_query_arg($args, $url));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['cod']) && $data['cod'] != 200) {
return array('error' => $data['message'] ?? '未知错误');
}
// 缓存数据10分钟
set_transient($transient_key, $data, 10 * MINUTE_IN_SECONDS);
return $data;
}
/**
* 获取天气预报数据
*/
public function get_forecast($city, $country = '', $units = 'metric', $days = 5) {
$location = $city;
if (!empty($country)) {
$location .= ',' . $country;
}
$transient_key = 'wmi_forecast_' . md5($location . $units . $days);
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
$url = $this->base_url . 'forecast';
$args = array(
'q' => $location,
'appid' => $this->api_key,
'units' => $units,
'cnt' => $days * 8, // 每3小时一个数据点
'lang' => $this->get_language_code()
);
$response = wp_remote_get(add_query_arg($args, $url));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['cod']) && $data['cod'] != 200) {
return array('error' => $data['message'] ?? '未知错误');
}
// 缓存数据30分钟
set_transient($transient_key, $data, 30 * MINUTE_IN_SECONDS);
return $data;
}
/**
* 根据IP获取位置信息
*/
public function get_location_by_ip() {
$transient_key = 'wmi_location_ip_' . $_SERVER['REMOTE_ADDR'];
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
// 使用ipapi.co服务(免费)
$response = wp_remote_get('https://ipapi.co/json/');
if (is_wp_error($response)) {
return array('city' => 'Beijing', 'country' => 'CN');
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
$location = array(
'city' => $data['city'] ?? 'Beijing',
'country' => $data['country'] ?? 'CN'
);
// 缓存24小时
set_transient($transient_key, $location, 24 * HOUR_IN_SECONDS);
return $location;
}
/**
* 获取语言代码
*/
private function get_language_code() {
$locale = get_locale();
$lang_map = array(
'zh_CN' => 'zh_cn',
'zh_TW' => 'zh_tw',
'en_US' => 'en',
'en_GB' => 'en',
'es_ES' => 'es',
'fr_FR' => 'fr',
'de_DE' => 'de',
'ja' => 'ja',
'ko_KR' => 'kr'
);
return $lang_map[$locale] ?? 'en';
}
/**
* 处理天气数据为前端可用格式
*/
public function process_weather_data($weather_data) {
if (isset($weather_data['error'])) {
return $weather_data;
}
$processed = array(
'location' => $weather_data['name'] . ', ' . ($weather_data['sys']['country'] ?? ''),
'temperature' => round($weather_data['main']['temp']),
'feels_like' => round($weather_data['main']['feels_like']),
'description' => $weather_data['weather'][0]['description'],
'icon' => $weather_data['weather'][0]['icon'],
'humidity' => $weather_data['main']['humidity'],
'pressure' => $weather_data['main']['pressure'],
'wind_speed' => $weather_data['wind']['speed'],
'wind_deg' => $weather_data['wind']['deg'],
'sunrise' => date('H:i', $weather_data['sys']['sunrise']),
'sunset' => date('H:i', $weather_data['sys']['sunset']),
'timestamp' => time()
);
return $processed;
}
}
在主插件类中添加天气短代码方法:
public function weather_shortcode($atts) {
$atts = shortcode_atts(array(
'city' => '',
'country' => '',
'units' => get_option('wmi_temperature_unit', 'metric'),
'show_forecast' => 'false',
'forecast_days' => 3,
'layout' => 'compact', // compact, detailed, card
'title' => '当前天气'
), $atts, 'weather_display');
// 如果没有指定城市,尝试根据IP获取
if (empty($atts['city'])) {
$weather_data = new WMI_Weather_Data();
$location = $weather_data->get_location_by_ip();
$atts['city'] = $location['city'];
$atts['country'] = $location['country'];
}
// 获取天气数据
$weather_api = new WMI_Weather_Data();
$current_weather = $weather_api->get_current_weather($atts['city'], $atts['country'], $atts['units']);
if (isset($current_weather['error'])) {
return '<div class="wmi-error">无法获取天气数据: ' . esc_html($current_weather['error']) . '</div>';
}
$processed_weather = $weather_api->process_weather_data($current_weather);
// 获取天气预报(如果需要)
$forecast_data = array();
if ($atts['show_forecast'] === 'true') {
$forecast = $weather_api->get_forecast($atts['city'], $atts['country'], $atts['units'], $atts['forecast_days']);
if (!isset($forecast['error'])) {
$forecast_data = $this->process_forecast_data($forecast);
}
}
// 生成输出HTML
ob_start();
?>
<div class="wmi-weather-container wmi-layout-<?php echo esc_attr($atts['layout']); ?>"
data-city="<?php echo esc_attr($atts['city']); ?>"
data-country="<?php echo esc_attr($atts['country']); ?>"
data-units="<?php echo esc_attr($atts['units']); ?>">
<div class="wmi-weather-header">
<h3 class="wmi-weather-title"><?php echo esc_html($atts['title']); ?></h3>
<div class="wmi-location"><?php echo esc_html($processed_weather['location']); ?></div>
</div>
<div class="wmi-current-weather">
<div class="wmi-weather-main">
<div class="wmi-temperature">
<span class="wmi-temp-value"><?php echo esc_html($processed_weather['temperature']); ?></span>
<span class="wmi-temp-unit">°<?php echo $atts['units'] === 'metric' ? 'C' : 'F'; ?></span>
</div>
<div class="wmi-weather-icon">
<img src="https://openweathermap.org/img/wn/<?php echo esc_attr($processed_weather['icon']); ?>@2x.png"
alt="<?php echo esc_attr($processed_weather['description']); ?>">
</div>
</div>
<div class="wmi-weather-details">
<div class="wmi-weather-desc"><?php echo esc_html($processed_weather['description']); ?></div>
<div class="wmi-weather-meta">
<span class="wmi-feels-like">体感温度: <?php echo esc_html($processed_weather['feels_like']); ?>°</span>
<span class="wmi-humidity">湿度: <?php echo esc_html($processed_weather['humidity']); ?>%</span>
<span class="wmi-wind">风速: <?php echo esc_html($processed_weather['wind_speed']); ?> m/s</span>
</div>
</div>
</div>
<?php if (!empty($forecast_data)): ?>
<div class="wmi-forecast">
<h4 class="wmi-forecast-title"><?php echo esc_html($atts['forecast_days']); ?>天预报</h4>
<div class="wmi-forecast-days">
<?php foreach ($forecast_data as $day): ?>
<div class="wmi-forecast-day">
<div class="wmi-forecast-date"><?php echo esc_html($day['date']); ?></div>
<div class="wmi-forecast-icon">
<img src="https://openweathermap.org/img/wn/<?php echo esc_attr($day['icon']); ?>.png"
alt="<?php echo esc_attr($day['description']); ?>">
</div>
<div class="wmi-forecast-temp">
<span class="wmi-forecast-temp-max"><?php echo esc_html($day['temp_max']); ?>°</span>
<span class="wmi-forecast-temp-min"><?php echo esc_html($day['temp_min']); ?>°</span>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="wmi-weather-footer">
<div class="wmi-sun-times">
<span class="wmi-sunrise">日出: <?php echo esc_html($processed_weather['sunrise']); ?></span>
<span class="wmi-sunset">日落: <?php echo esc_html($processed_weather['sunset']); ?></span>
</div>
<div class="wmi-update-time">
更新时间: <?php echo date('H:i', $processed_weather['timestamp']); ?>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
private function process_forecast_data($forecast_data) {
$daily_data = array();
$grouped_by_day = array();
// 按日期分组
foreach ($forecast_data['list'] as $item) {
$date = date('Y-m-d', $item['dt']);
if (!isset($grouped_by_day[$date])) {
$grouped_by_day[$date] = array();
}
$grouped_by_day[$date][] = $item;
}
// 处理每天的数据
$count = 0;
foreach ($grouped_by_day as $date => $day_items) {
if ($count >= 5) break; // 最多显示5天
$temps = array();
$icons = array();
$descriptions = array();
foreach ($day_items as $item) {
$temps[] = $item['main']['temp'];
$icons[] = $item['weather'][0]['icon'];
$descriptions[] = $item['weather'][0]['description'];
}
// 获取最常见的图标和描述
$icon_counts = array_count_values($icons);
arsort($icon_counts);
$most_common_icon = key($icon_counts);
$desc_counts = array_count_values($descriptions);
arsort($desc_counts);
$most_common_desc = key($desc_counts);
$daily_data[] = array(
'date' => date('m/d', strtotime($date)),
'day' => date('D', strtotime($date)),
'temp_max' => round(max($temps)),
'temp_min' => round(min($temps)),
'icon' => $most_common_icon,
'description' => $most_common_desc
);
$count++;
}
return $daily_data;
}
## 第五部分:地图功能实现
### 5.1 创建地图数据类
在插件目录下创建`includes/class-map-data.php`:
<?php
if (!defined('ABSPATH')) {
exit;
}
class WMI_Map_Data {
/**
* 获取位置坐标
*/
public function get_coordinates($location) {
$transient_key = 'wmi_coordinates_' . md5($location);
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
// 使用Nominatim(OpenStreetMap的搜索服务)
$url = 'https://nominatim.openstreetmap.org/search';
$args = array(
'q' => $location,
'format' => 'json',
'limit' => 1,
'addressdetails' => 1
);
$response = wp_remote_get(add_query_arg($args, $url), array(
'headers' => array(
'User-Agent' => 'WordPress Weather Map Integration/1.0'
)
));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (empty($data)) {
return array('error' => '未找到位置信息');
}
$coordinates = array(
'lat' => $data[0]['lat'],
'lon' => $data[0]['lon'],
'display_name' => $data[0]['display_name'],
'address' => $data[0]['address']
);
// 缓存30天
set_transient($transient_key, $coordinates, 30 * DAY_IN_SECONDS);
return $coordinates;
}
/**
* 获取多个标记点
*/
public function get_multiple_markers($locations) {
$markers = array();
foreach ($locations as $location) {
if (is_array($location) && isset($location['lat'], $location['lng'])) {
$markers[] = array(
'lat' => $location['lat'],
'lng' => $location['lng'],
'title' => $location['title'] ?? '',
'description' => $location['description'] ?? ''
);
} else {
$coords = $this->get_coordinates($location);
if (!isset($coords['error'])) {
$markers[] = array(
'lat' => $coords['lat'],
'lng' => $coords['lon'],
'title' => is_array($location) ? ($location['title'] ?? '') : $location,
'description' => $coords['display_name']
);
}
}
}
return $markers;
}
/**
* 计算地图边界
*/
public function calculate_bounds($markers) {
if (empty($markers)) {
return array(
'south' => 0,
'west' => 0,
'north' => 0,
'east' => 0
);
}
$lats = array_column($markers, 'lat');
$lngs = array_column($markers, 'lng');
return array(
'south' => min($lats),
'west' => min($lngs),
'north' => max($lats),
'east' => max($lngs)
);
}
}
### 5.2 创建地图显示短代码
在主插件类中添加地图短代码方法:
public function map_shortcode($atts) {
$atts = shortcode_atts(array(
'location' => '',
'lat' => '',
'lng' => '',
'zoom' => get_option('wmi_default_zoom', 13),
'height' => get_option('wmi_map_height', '400px'),
'width' => get_option('wmi_map_width', '100%'),
'markers' => '', // JSON格式或逗号分隔
'show_search' => 'false',
'show_controls' => 'true',
'map_style' => 'streets', // streets, satellite, terrain
'title' => '位置地图'
), $atts, 'map_display');
// 生成唯一ID
$map_id = 'wmi-map-' . uniqid();
// 处理位置数据
$locations = array();
if (!empty($atts['markers'])) {
// 尝试解析JSON
$markers_json = json_decode($atts['markers'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$locations = $markers_json;
} else {
// 逗号分隔的字符串
$marker_list = explode(',', $atts['markers']);
foreach ($marker_list as $marker) {
$locations[] = trim($marker);
}
}
} elseif (!empty($atts['location'])) {
$locations[] = $atts['location'];
} elseif (!empty($atts['lat']) && !empty($atts['lng'])) {
$locations[] = array(
'lat' => $atts['lat'],
'lng' => $atts['lng'],
'title' => $atts['title']
);
} else {
// 默认位置
$weather_data = new WMI_Weather_Data();
$default_location = $weather_data->get_location_by_ip();
$locations[] = $default_location['city'] . ', ' . $default_location['country'];
}
// 获取坐标
$map_api = new WMI_Map_Data();
$markers = $map_api->get_multiple_markers($locations);
// 计算地图边界
$bounds = $map_api->calculate_bounds($markers);
ob_start();
?>
<div class="wmi-map-container" id="<?php echo esc_attr($map_id); ?>-container">
<?php if (!empty($atts['title'])): ?>
<h3 class="wmi-map-title"><?php echo esc_html($atts['title']); ?></h3>
<?php endif; ?>
<?php if ($atts['show_search'] === 'true'): ?>
<div class="wmi-map-search">
<input type="text" id="<?php echo esc_attr($map_id); ?>-search"
placeholder="搜索地点..." class="wmi-map-search-input">
<button type="button" class="wmi-map-search-button">搜索</button>
</div>
<?php endif; ?>
<div class="wmi-map"
id="<?php echo esc_attr($map_id); ?>"
style="height: <?php echo esc_attr($atts['height']); ?>;
width: <?php echo esc_attr($atts['width']); ?>;">
</div>
<div class="wmi-map-info">
<?php if (!empty($markers) && count($markers) === 1): ?>
<div class="wmi-location-info">
<strong><?php echo esc_html($markers[0]['title'] ?: '位置'); ?>:</strong>
<span><?php echo esc_html($markers[0]['description']); ?></span>
</div>
<?php endif; ?>
</div>
</div>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
if (typeof L !== 'undefined') {
initWmiMap('<?php echo esc_js($map_id); ?>', {
markers: <?php echo json_encode($markers); ?>,
bounds: <?php echo json_encode($bounds); ?>,
zoom: <?php echo intval($atts['zoom']); ?>,
showControls: <?php echo $atts['show_controls'] === 'true' ? 'true' : 'false'; ?>,
mapStyle: '<?php echo esc_js($atts['map_style']); ?>'
});
}
});
</script>
<?php
return ob_get_clean();
}
### 5.3 创建组合短代码
public function weather_map_combo_shortcode($atts) {
$atts = shortcode_atts(array(
'location' => '',
'city' => '',
'country' => '',
'layout' => 'side-by-side', // side-by-side, map-above, weather-above
'height' => '500px',
'show_forecast' => 'true',
'show_search' => 'true',
'title' => '天气与位置'
), $atts, 'weather_map_combo');
// 确定位置
if (!empty($atts['location'])) {
$location_parts = explode(',', $atts['location']);
$atts['city'] = trim($location_parts[0]);
if (isset($location_parts[1])) {
$atts['country'] = trim($location_parts[1]);
}
}
ob_start();
?>
<div class="wmi-combo-container wmi-layout-<?php echo esc_attr($atts['layout']); ?>">
<h2 class="wmi-combo-title"><?php echo esc_html($atts['title']); ?></h2>
<div class="wmi-combo-content">
<div class="wmi-combo-weather">
<?php
echo $this->weather_shortcode(array(
'city' => $atts['city'],
'country' => $atts['country'],
'show_forecast' => $atts['show_forecast'],
'layout' => 'detailed',
'title' => ''
));
?>
</div>
<div class="wmi-combo-map">
<?php
$location_str = !empty($atts['country']) ?
$atts['city'] . ', ' . $atts['country'] :
$atts['city'];
echo $this->map_shortcode(array(
'location' => $location_str,
'height' => $atts['height'],
'show_search' => $atts['show_search'],
'title' => ''
));
?>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
## 第六部分:前端资源加载与样式
### 6.1 注册和加载脚本样式
在主插件类中添加以下方法:
public function enqueue_frontend_scripts() {
// 加载Leaflet地图库
wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css', array(), '1.7.1');
wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', array(), '1.7.1', true);
// 加载插件样式
wp_enqueue_style('wmi-frontend-style', WMI_PLUGIN_URL . 'assets/css/frontend.css', array(), WMI_VERSION);
// 加载插件脚本
wp_enqueue_script('wmi-frontend-script', WMI_PLUGIN_URL . 'assets/js/frontend.js', array('jquery', 'leaflet-js'), WMI_VERSION, true);
// 本地化脚本
wp_localize_script('wmi-frontend-script', 'wmi_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wmi_ajax_nonce'),
'default_location' => $this->get_default_location(),
'strings' => array(
'loading' => __('加载中...', 'weather-map-integration'),
'error' => __('发生错误', 'weather-map-integration'),
'search_placeholder' => __('输入地点


