From 681ad5320f999cdc54cae3fc5cd7f6485a698cf5 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Fri, 14 Feb 2025 11:58:16 +0800 Subject: [PATCH 01/12] style: update time display format - Replace local timezone display with UTC in the weather display components - Adjust time formatting in both city and weather art views This change standardizes the time format across the application, making it clear that the displayed times are in UTC, aiding user understanding and consistency in time representation. --- app/views/cities/show.html.erb | 5 +++-- app/views/weather_arts/show.html.erb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/cities/show.html.erb b/app/views/cities/show.html.erb index bd87b53..9040c6f 100644 --- a/app/views/cities/show.html.erb +++ b/app/views/cities/show.html.erb @@ -35,7 +35,8 @@ <%= "#{@city&.country&.emoji + " " || ""}#{@city&.country&.name}" %>, <%= @city.region %>
- <%= @city.timezone.present? ? Time.current.in_time_zone(@city.timezone).strftime("%Y-%m-%d %H:%M") : "Timezone undefined" %> + <%#= @city.timezone.present? ? Time.current.in_time_zone(@city.timezone).strftime("%Y-%m-%d %H:%M") : "Timezone undefined" %> + <%= @city&.state&.name %>
@@ -109,7 +110,7 @@
<%= art.temperature %>°C
-
<%= art.created_at.strftime("%H:%M") %>
+
UTC <%= art.created_at.strftime("%H:%M") %>
<%= art.weather_date.strftime("%B %d, %Y") %>
diff --git a/app/views/weather_arts/show.html.erb b/app/views/weather_arts/show.html.erb index dd85448..9c46497 100644 --- a/app/views/weather_arts/show.html.erb +++ b/app/views/weather_arts/show.html.erb @@ -55,7 +55,7 @@ <%= @weather_art.weather_date.strftime("%B %d, %Y") %>
- <%= @weather_art.created_at.strftime("%H:%M") %> + UTC <%= @weather_art.created_at.strftime("%H:%M") %>
From f43a6b4698be4a239998f65413b56a08fe34fc15 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Fri, 14 Feb 2025 13:42:42 +0800 Subject: [PATCH 02/12] feat: add formatted time method for weather_art - Introduce a new method `formatted_time` in the `WeatherArt` model - Update various views to use this new method for date and time display - Support formatting in local time zones or UTC This update enhances the time representation for weather data, ensuring that displayed times can reflect the user's local timezone or remain fixed at UTC. This improves the usability of the application for users in different regions. --- app/models/weather_art.rb | 29 ++++++++++++++++++++++++++++ app/views/arts/index.html.erb | 4 ++-- app/views/cities/_city.html.erb | 2 +- app/views/cities/index.html.erb | 2 +- app/views/cities/show.html.erb | 4 ++-- app/views/home/_arts.html.erb | 2 +- app/views/weather_arts/show.html.erb | 4 ++-- 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/app/models/weather_art.rb b/app/models/weather_art.rb index f624037..6c37484 100644 --- a/app/models/weather_art.rb +++ b/app/models/weather_art.rb @@ -68,6 +68,35 @@ class WeatherArt < ApplicationRecord end end + def formatted_time(type = :date, use_local_timezone = false) + # 获取时区 + timezone_info = city&.country&.timezones.present? ? + eval(self.city.country.timezones).first : + { "zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00" } + + # 设置时区对象 + time_zone = ActiveSupport::TimeZone[timezone_info["zoneName"]] || + ActiveSupport::TimeZone["UTC"] + + case type + when :date + # 格式化日期 + self&.weather_date&.strftime("%B %d, %Y") + when :time + # 获取时间 + time = updated_at + + if use_local_timezone + # 使用本地时区 + local_time = time.in_time_zone(time_zone) + "#{local_time.strftime('%H:%M')} #{timezone_info['gmtOffsetName']}" + else + # 使用 UTC + "#{time.utc.strftime('%H:%M')} UTC" + end + end + end + def image_url image.attached? ? image.blob : nil end diff --git a/app/views/arts/index.html.erb b/app/views/arts/index.html.erb index 42a55b1..5499093 100644 --- a/app/views/arts/index.html.erb +++ b/app/views/arts/index.html.erb @@ -28,7 +28,7 @@
Latest from <%= featured_art.city.name %>, <%= featured_art.city.country.name %> - <%= featured_art.weather_date.strftime("%B %d, %Y") %> + <%= featured_art.formatted_time(:date) %>
<% end %> @@ -139,7 +139,7 @@ <%= art.city.name %>

- <%= art.weather_date.strftime("%B %d, %Y") %> + <%= art.formatted_time(:date, true) %>

diff --git a/app/views/cities/_city.html.erb b/app/views/cities/_city.html.erb index 438d472..06e2753 100644 --- a/app/views/cities/_city.html.erb +++ b/app/views/cities/_city.html.erb @@ -51,7 +51,7 @@ - <%= city.latest_weather_art.weather_date.strftime("%b %d, %Y") %> + <%= city.latest_weather_art.formatted_time(:date) %>
diff --git a/app/views/cities/index.html.erb b/app/views/cities/index.html.erb index 3706f00..b5aae1c 100644 --- a/app/views/cities/index.html.erb +++ b/app/views/cities/index.html.erb @@ -29,7 +29,7 @@ <%= featured_art.city.name %>, <%= featured_art.city.country.name %> - <%= featured_art.weather_date.strftime("%B %d, %Y") %> + <%= featured_art.formatted_time(:date) %> <% end %> <%= render 'cities/search_city' %> diff --git a/app/views/cities/show.html.erb b/app/views/cities/show.html.erb index 9040c6f..c26d5c9 100644 --- a/app/views/cities/show.html.erb +++ b/app/views/cities/show.html.erb @@ -110,8 +110,8 @@
<%= art.temperature %>°C
-
UTC <%= art.created_at.strftime("%H:%M") %>
-
<%= art.weather_date.strftime("%B %d, %Y") %>
+
<%= art.formatted_time(:date) %>
+
<%= art.formatted_time(:time, true) %>
diff --git a/app/views/home/_arts.html.erb b/app/views/home/_arts.html.erb index bf432bb..bb127f0 100644 --- a/app/views/home/_arts.html.erb +++ b/app/views/home/_arts.html.erb @@ -11,7 +11,7 @@

<%= art.city.name %>

- <%= art.weather_date.strftime("%B %d, %Y") %> + <%= art.formatted_time(:date) %>

diff --git a/app/views/weather_arts/show.html.erb b/app/views/weather_arts/show.html.erb index 9c46497..7e6547f 100644 --- a/app/views/weather_arts/show.html.erb +++ b/app/views/weather_arts/show.html.erb @@ -52,10 +52,10 @@
- <%= @weather_art.weather_date.strftime("%B %d, %Y") %> + <%= @weather_art.formatted_time(:date) %>
- UTC <%= @weather_art.created_at.strftime("%H:%M") %> + <%= @weather_art.formatted_time(:time, true) %>
From 95f94cb73bd6cdc32f2b30e3e022000cedcba163 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Fri, 14 Feb 2025 14:56:51 +0800 Subject: [PATCH 03/12] feat: add statistics card component and refactor views - Introduce a new partial for displaying weather statistics in a consistent card format. - Refactor the city show page to utilize the new statistics card partial, simplifying the code structure. - Update layout and styling for improved UX and maintainability. These changes enhance the maintainability of the codebase by promoting reuse of the statistics card component and improving the overall presentation of weather data for cities. --- app/views/cities/_stat_card.html.erb | 13 ++ app/views/cities/show.html.erb | 178 +++++++++----------------- app/views/weather_arts/_card.html.erb | 44 +++++++ 3 files changed, 117 insertions(+), 118 deletions(-) create mode 100644 app/views/cities/_stat_card.html.erb create mode 100644 app/views/weather_arts/_card.html.erb diff --git a/app/views/cities/_stat_card.html.erb b/app/views/cities/_stat_card.html.erb new file mode 100644 index 0000000..f62f32e --- /dev/null +++ b/app/views/cities/_stat_card.html.erb @@ -0,0 +1,13 @@ + +
+
+ <%= weather_stat_icon(stat[:icon]) %> +
<%= stat[:title] %>
+
+
+
+ <%= stat[:value] %> +
+
+
<%= stat[:desc] %>
+
diff --git a/app/views/cities/show.html.erb b/app/views/cities/show.html.erb index c26d5c9..c9d4e2a 100644 --- a/app/views/cities/show.html.erb +++ b/app/views/cities/show.html.erb @@ -1,19 +1,18 @@ -
- - <% if @city.latest_weather_art&.image&.attached? %> -
- <%= image_tag @city.latest_weather_art.webp_image.processed, - class: "absolute w-full h-full object-cover scale-110 filter blur-2xl opacity-25" %> -
-
- <% end %> +
+ +
+ + <% if @city.latest_weather_art&.image&.attached? %> +
+ <%= image_tag @city.latest_weather_art.webp_image.processed, + class: "w-full h-full object-cover object-center" %> +
+
+ <% end %> - -
- -
+
<%= link_to cities_path, - class: "btn btn-ghost btn-lg gap-2 bg-base-100/50 backdrop-blur-sm hover:bg-base-100/70 transition-all duration-300" do %> + class: "inline-flex items-center btn btn-ghost gap-2 pt-6 mb-8" do %> @@ -21,132 +20,75 @@ <% end %>
- -
-
-

- +
+
+ +
+

<%= @city.localized_name %> - -

+

-
-
- <%= "#{@city&.country&.emoji + " " || ""}#{@city&.country&.name}" %>, <%= @city.region %> +
+
+ <%= "#{@city&.country&.emoji + " " || ""}#{@city&.country&.name}" %> +
+
+ <%= @city&.state&.name %> +
-
- <%#= @city.timezone.present? ? Time.current.in_time_zone(@city.timezone).strftime("%Y-%m-%d %H:%M") : "Timezone undefined" %> - <%= @city&.state&.name %> + + +
+ <%= render partial: 'cities/stat_card', collection: [ + { + icon: 'temperature', + title: 'Latest Weather', + value: "#{@city.latest_weather_art&.temperature}°C", + desc: @city.latest_weather_art&.description + }, + { + icon: 'location', + title: 'Coordinates', + value: "#{@city.latitude}°N, #{@city.longitude}°E", + desc: 'Geographical Location' + }, + { + icon: 'history', + title: 'Records', + value: @city.weather_arts.count, + desc: 'Total Weather Arts' + } + ], as: :stat %>
+ + <%= render 'cities/admin_panel' %>
- - -
-
-
- <%= weather_stat_icon("temperature") %> -
Latest Weather
-
-
<%= @city.latest_weather_art&.temperature %>°C
-
<%= @city.latest_weather_art&.description %>
-
- -
-
- <%= weather_stat_icon("location") %> -
Coordinates
-
-
- <%= @city.latitude %>°N, - <%= @city.longitude %>°E -
-
Geographical Location
-
- -
-
- <%= weather_stat_icon("history") %> -
Records
-
-
<%= @city.weather_arts.count %>
-
Total Weather Arts
-
-
- - <%= render 'cities/admin_panel' %>
+
- -
+ +
+
-

- Weather Art History -

-
+

Weather Art History

+
- Last Updated: - - <%= time_ago_in_words(@city.last_weather_fetch) if @city.last_weather_fetch %> - + Last Updated: <%= time_ago_in_words(@city.last_weather_fetch) if @city.last_weather_fetch %>
- <% @city.weather_arts.order(weather_date: :desc).each do |art| %> -
-
- <% if art.image.attached? %> - <%= image_tag art.preview_image.processed, - class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %> -
-
-
<%= art.temperature %>°C
-
-
<%= art.formatted_time(:date) %>
-
<%= art.formatted_time(:time, true) %>
-
-
-
- <% end %> -
- -
-

- <%= weather_description_icon(art.description) %> - <%= art.description %> -

- -
-
- <%= weather_stat_icon("humidity") %> - Humidity: <%= art.humidity %>% -
-
- <%= weather_stat_icon("wind") %> - Wind: <%= art.wind_scale %> -
-
- - <%= link_to city_weather_art_path(@city, art), - class: "btn btn-primary btn-block" do %> - View Details - - - - <% end %> -
-
- <% end %> + <%= render partial: 'weather_arts/card', collection: @city.weather_arts.order(weather_date: :desc), as: :weather_art %>
-
\ No newline at end of file +
diff --git a/app/views/weather_arts/_card.html.erb b/app/views/weather_arts/_card.html.erb new file mode 100644 index 0000000..bead5f5 --- /dev/null +++ b/app/views/weather_arts/_card.html.erb @@ -0,0 +1,44 @@ + +
+
+ <% if weather_art.image.attached? %> + <%= image_tag weather_art.preview_image.processed, + class: "w-full h-full object-cover transform hover:scale-105 transition-transform duration-500" %> +
+
+
<%= weather_art.temperature %>°C
+
+
<%= weather_art.formatted_time(:date) %>
+
<%= weather_art.formatted_time(:time, true) %>
+
+
+
+ <% end %> +
+ +
+

+ <%= weather_description_icon(weather_art.description) %> + <%= weather_art.description %> +

+ +
+
+ <%= weather_stat_icon("humidity") %> + Humidity: <%= weather_art.humidity %>% +
+
+ <%= weather_stat_icon("wind") %> + Wind: <%= weather_art.wind_scale %> +
+
+ + <%= link_to city_weather_art_path(weather_art.city, weather_art), + class: "btn btn-primary btn-block" do %> + View Details + + + + <% end %> +
+
\ No newline at end of file From df456d10319d10dcb47350b866adb27c0175349e Mon Sep 17 00:00:00 2001 From: songtianlun Date: Fri, 14 Feb 2025 18:05:03 +0800 Subject: [PATCH 04/12] feat: add map feature to city show page - Implement MapController for displaying city maps - Add map rendering in the city show view - Include weather arts in the city show controller - Update asset pipeline to include Leaflet CSS and JS This commit introduces a map feature that allows users to view geographical information related to cities. The map is integrated with weather arts data, enhancing the overall functionality of the city show page. --- .../stylesheets/application.tailwind.css | 1 + app/controllers/cities_controller.rb | 1 + app/javascript/controllers/index.js | 3 + app/javascript/controllers/map_controller.js | 58 +++++++++++++++++++ app/views/cities/_map.html.erb | 8 +++ app/views/cities/show.html.erb | 4 +- config/environments/development.rb | 2 + config/initializers/assets.rb | 6 ++ package.json | 3 +- yarn.lock | 5 ++ 10 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 app/javascript/controllers/map_controller.js create mode 100644 app/views/cities/_map.html.erb diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css index 107281b..e572ea2 100644 --- a/app/assets/stylesheets/application.tailwind.css +++ b/app/assets/stylesheets/application.tailwind.css @@ -1,4 +1,5 @@ @import "photoswipe/dist/photoswipe.css"; +/*@import "leaflet/dist/leaflet.css";*/ @tailwind base; @tailwind components; diff --git a/app/controllers/cities_controller.rb b/app/controllers/cities_controller.rb index 05a6c71..aaf3976 100644 --- a/app/controllers/cities_controller.rb +++ b/app/controllers/cities_controller.rb @@ -35,6 +35,7 @@ class CitiesController < ApplicationController def show @city = City.friendly.find(params[:id]) + @arts = @city.weather_arts.order(weather_date: :desc).includes([ :image_attachment ]) ahoy.track "View City", { city_id: @city.id, name: @city.name, diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 61604f3..afb6930 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -8,9 +8,12 @@ import PhotoSwipeLightBoxController from "./photo_swipe_lightbox_controller" import FlashMessageController from "./flash_controller" import SearchController from "./search_controller" import PageLoadTimeController from "./page_load_time_controller" +import MapController from "./map_controller" application.register("hello", HelloController) application.register("photo-swipe-lightbox", PhotoSwipeLightBoxController) application.register("flash", FlashMessageController) application.register("search", SearchController) application.register("page-load-time", PageLoadTimeController) +application.register("map", MapController) + diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js new file mode 100644 index 0000000..f103591 --- /dev/null +++ b/app/javascript/controllers/map_controller.js @@ -0,0 +1,58 @@ +import { Controller } from "@hotwired/stimulus" +import L from 'leaflet' + +import 'leaflet/dist/leaflet.css' +import iconUrl from 'leaflet/dist/images/marker-icon.png'; +import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png'; +// import iconRetinaUrl from '../images/leaflet/marker-icon-2x.png'; +import shadowUrl from 'leaflet/dist/images/marker-shadow.png'; + +export default class extends Controller { + static values = { + latitude: Number, + longitude: Number + } + + init() { + } + + connect() { + + delete L.Icon.Default.prototype._getIconUrl + L.Icon.Default.mergeOptions({ + // iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), + // iconUrl: require('leaflet/dist/images/marker-icon.png'), + // shadowUrl: require('leaflet/dist/images/marker-shadow.png') + iconRetinaUrl: iconRetinaUrl, + iconUrl: iconUrl, + shadowUrl: shadowUrl, + }); + + this.initializeMap() + } + + initializeMap() { + // 创建地图实例 + this.map = L.map(this.element).setView( + [this.latitudeValue, this.longitudeValue], + 4 // 缩放级别 + ) + + // 添加地图图层 + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(this.map) + + // 添加标记 + L.marker([this.latitudeValue, this.longitudeValue]) + .addTo(this.map) + .bindPopup(document.querySelector('h1').textContent) + .openPopup() + } + + disconnect() { + if (this.map) { + this.map.remove() + } + } +} \ No newline at end of file diff --git a/app/views/cities/_map.html.erb b/app/views/cities/_map.html.erb new file mode 100644 index 0000000..64de418 --- /dev/null +++ b/app/views/cities/_map.html.erb @@ -0,0 +1,8 @@ +
+
+
+
\ No newline at end of file diff --git a/app/views/cities/show.html.erb b/app/views/cities/show.html.erb index c9d4e2a..9997de4 100644 --- a/app/views/cities/show.html.erb +++ b/app/views/cities/show.html.erb @@ -61,6 +61,8 @@ ], as: :stat %>
+ <%= render 'cities/map', city: @city %> + <%= render 'cities/admin_panel' %>
@@ -86,7 +88,7 @@
- <%= render partial: 'weather_arts/card', collection: @city.weather_arts.order(weather_date: :desc), as: :weather_art %> + <%= render partial: 'weather_arts/card', collection: @arts, as: :weather_art %>
diff --git a/config/environments/development.rb b/config/environments/development.rb index 415bcf5..5389bbd 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -79,4 +79,6 @@ Rails.application.configure do # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. # config.generators.apply_rubocop_autocorrect_after_generate! + + config.serve_static_assets = true end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4873244..42f11c9 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,3 +5,9 @@ Rails.application.config.assets.version = "1.0" # Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path +# Rails.application.config.assets.paths << Rails.root.join("app/assets/builds") +# Rails.application.config.assets.paths << Rails.root.join("node_modules") +# Rails.application.config.assets.precompile += %w( *.png *.jpg *.jpeg *.gif ) +# Rails.application.config.assets.paths << Rails.root.join("app/assets/builds") +# Rails.application.config.assets.precompile += %w( *.png *.jpg *.jpeg *.gif ) + diff --git a/package.json b/package.json index 8b11caf..bec12fc 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "esbuild": "^0.24.2" }, "scripts": { - "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets --loader:.woff=file --loader:.woff2=file --loader:.ttf=file --loader:.eot=file --asset-names=[name]-[hash]", + "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets --loader:.woff=file --loader:.woff2=file --loader:.ttf=file --loader:.eot=file --loader:.png=file --asset-names=[name]-[hash].digested --chunk-names=[name]-[hash].digested", "build:css": "tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css && sass ./app/assets/stylesheets/active_admin.scss:./app/assets/builds/active_admin.css --no-source-map --load-path=node_modules" }, "dependencies": { @@ -18,6 +18,7 @@ "autoprefixer": "^10.4.20", "jquery": "^3.7.1", "jquery-ui": "^1.14.1", + "leaflet": "^1.9.4", "photoswipe": "^5.4.4", "postcss": "^8.5.1", "sass": "^1.83.4", diff --git a/yarn.lock b/yarn.lock index 29ceef2..210bf08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -739,6 +739,11 @@ jquery-ujs@^1.2.2: resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== +leaflet@^1.9.4: + version "1.9.4" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d" + integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA== + lilconfig@^3.0.0, lilconfig@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" From 9ac7dd46afdde708f18477688761cc69701ed406 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 00:19:04 +0800 Subject: [PATCH 05/12] feat: migrate to OpenLayers for map display - Replaced Leaflet with OpenLayers for improved map rendering - Added OpenLayers CSS and removed Leaflet CSS - Updated map controller to use OpenLayers API - Added marker icon in public directory - Added scopes and associations for weather art in City model This change migrates the map display from Leaflet to OpenLayers, providing better performance and more features. It also introduces new model associations for weather arts, allowing to sort cities by latest weather updates. --- .../stylesheets/application.tailwind.css | 27 ++++ app/javascript/controllers/map_controller.js | 79 +++++----- app/models/city.rb | 28 ++++ app/views/cities/_map.html.erb | 18 ++- package.json | 2 +- public/marker-icon.svg | 3 + yarn.lock | 136 +++++++++++++++++- 7 files changed, 244 insertions(+), 49 deletions(-) create mode 100644 public/marker-icon.svg diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css index e572ea2..f73b6ad 100644 --- a/app/assets/stylesheets/application.tailwind.css +++ b/app/assets/stylesheets/application.tailwind.css @@ -1,5 +1,6 @@ @import "photoswipe/dist/photoswipe.css"; /*@import "leaflet/dist/leaflet.css";*/ +@import 'ol/ol.css'; @tailwind base; @tailwind components; @@ -42,4 +43,30 @@ .animate-spin { animation: spin 1s linear infinite; transform-origin: center center; +} + +.map-container { + width: 100%; + height: 400px; + position: relative; +} + +/* 使弹出窗口在地图上可见 */ +.ol-overlay-container { + will-change: transform; + position: absolute; + transform: translate(-50%, -100%); + pointer-events: auto; +} + +/* 添加一个小箭头 */ +.ol-overlay-container::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -8px; + border-width: 8px; + border-style: solid; + border-color: white transparent transparent transparent; } \ No newline at end of file diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js index f103591..65dde67 100644 --- a/app/javascript/controllers/map_controller.js +++ b/app/javascript/controllers/map_controller.js @@ -1,11 +1,15 @@ import { Controller } from "@hotwired/stimulus" -import L from 'leaflet' +import Map from 'ol/Map' +import View from 'ol/View' +import TileLayer from 'ol/layer/Tile' +import OSM from 'ol/source/OSM' +import { fromLonLat } from 'ol/proj' +import Feature from 'ol/Feature' +import Point from 'ol/geom/Point' +import { Vector as VectorLayer } from 'ol/layer' +import { Vector as VectorSource } from 'ol/source' +import { Style, Icon } from 'ol/style' -import 'leaflet/dist/leaflet.css' -import iconUrl from 'leaflet/dist/images/marker-icon.png'; -import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png'; -// import iconRetinaUrl from '../images/leaflet/marker-icon-2x.png'; -import shadowUrl from 'leaflet/dist/images/marker-shadow.png'; export default class extends Controller { static values = { @@ -13,46 +17,45 @@ export default class extends Controller { longitude: Number } - init() { - } - connect() { - - delete L.Icon.Default.prototype._getIconUrl - L.Icon.Default.mergeOptions({ - // iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), - // iconUrl: require('leaflet/dist/images/marker-icon.png'), - // shadowUrl: require('leaflet/dist/images/marker-shadow.png') - iconRetinaUrl: iconRetinaUrl, - iconUrl: iconUrl, - shadowUrl: shadowUrl, - }); - this.initializeMap() } initializeMap() { // 创建地图实例 - this.map = L.map(this.element).setView( - [this.latitudeValue, this.longitudeValue], - 4 // 缩放级别 - ) + const map = new Map({ + target: this.element, + layers: [ + new TileLayer({ + source: new OSM() + }) + ], + view: new View({ + center: fromLonLat([this.longitudeValue, this.latitudeValue]), + zoom: 4 + }) + }) - // 添加地图图层 - L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' - }).addTo(this.map) + // 添加标记点 + const marker = new Feature({ + geometry: new Point(fromLonLat([this.longitudeValue, this.latitudeValue])) + }) - // 添加标记 - L.marker([this.latitudeValue, this.longitudeValue]) - .addTo(this.map) - .bindPopup(document.querySelector('h1').textContent) - .openPopup() - } + const vectorSource = new VectorSource({ + features: [marker] + }) - disconnect() { - if (this.map) { - this.map.remove() - } + const vectorLayer = new VectorLayer({ + source: vectorSource, + style: new Style({ + image: new Icon({ + anchor: [0.5, 1], + src: '/marker-icon.svg', // 确保在public目录下有这个图标 + scale: 1.5 + }) + }) + }) + + map.addLayer(vectorLayer) } } \ No newline at end of file diff --git a/app/models/city.rb b/app/models/city.rb index 4d7f95b..ee590a3 100644 --- a/app/models/city.rb +++ b/app/models/city.rb @@ -107,6 +107,34 @@ class City < ApplicationRecord ) } + # 定义 latest_weather_art 关联 + has_one :latest_weather_art, -> { order(weather_date: :desc) }, + class_name: 'WeatherArt' + + # 包含最新天气艺术的 scope + scope :with_latest_weather_art, -> { + includes(:latest_weather_art) + } + + # 只获取有最新天气艺术的城市 + scope :has_weather_art, -> { + joins(:weather_arts).distinct + } + + # 按最新天气更新时间排序 + scope :order_by_latest_weather, -> { + joins(:weather_arts) + .group('cities.id') + .order('MAX(weather_arts.weather_date) DESC') + } + + # 获取最近24小时内更新过天气的城市 + scope :recently_updated, -> { + joins(:weather_arts) + .where('weather_arts.weather_date > ?', 24.hours.ago) + .distinct + } + def to_s name diff --git a/app/views/cities/_map.html.erb b/app/views/cities/_map.html.erb index 64de418..80a25da 100644 --- a/app/views/cities/_map.html.erb +++ b/app/views/cities/_map.html.erb @@ -1,8 +1,14 @@ -
-
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/package.json b/package.json index bec12fc..10c2467 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "autoprefixer": "^10.4.20", "jquery": "^3.7.1", "jquery-ui": "^1.14.1", - "leaflet": "^1.9.4", + "ol": "^10.4.0", "photoswipe": "^5.4.4", "postcss": "^8.5.1", "sass": "^1.83.4", diff --git a/public/marker-icon.svg b/public/marker-icon.svg new file mode 100644 index 0000000..f8b3554 --- /dev/null +++ b/public/marker-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 210bf08..fe11816 100644 --- a/yarn.lock +++ b/yarn.lock @@ -323,6 +323,11 @@ "@parcel/watcher-win32-ia32" "2.5.0" "@parcel/watcher-win32-x64" "2.5.0" +"@petamoriken/float16@^3.4.7": + version "3.9.1" + resolved "https://registry.yarnpkg.com/@petamoriken/float16/-/float16-3.9.1.tgz#f1239d6721f25c1bf921dde0d7d678efef9c1de2" + integrity sha512-j+ejhYwY6PeB+v1kn7lZFACUIG97u90WxMuGosILFsl9d4Ovi0sjk0GlPfoEcx+FzvXZDAfioD+NGnnPamXgMA== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" @@ -333,6 +338,11 @@ resolved "https://registry.npmjs.org/@rails/actioncable/-/actioncable-7.2.201.tgz" integrity sha512-wsTdWoZ5EfG5k3t7ORdyQF0ZmDEgN4aVPCanHAiNEwCROqibSZMXXmCbH7IDJUVri4FOeAVwwbPINI7HVHPKBw== +"@types/rbush@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/rbush/-/rbush-4.0.0.tgz#b327bf54952e9c924ea6702c36904c2ce1d47f35" + integrity sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -458,11 +468,36 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.0.0.tgz#03ff6b1b5aec9bb3cf1ed82400c2790dfcd01d2d" + integrity sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow== + color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-parse@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-2.0.2.tgz#37b46930424924060988edf25b24e6ffb4a1dc3f" + integrity sha512-eCtOz5w5ttWIUcaKLiktF+DxZO1R9KLNY/xhbV6CkhM7sR3GhVghmt6X6yOnzeaM24po+Z9/S1apbXMwA3Iepw== + dependencies: + color-name "^2.0.0" + +color-rgba@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/color-rgba/-/color-rgba-3.0.0.tgz#77090bdcdb2951c1735e20099ddd50401675375b" + integrity sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg== + dependencies: + color-parse "^2.0.0" + color-space "^2.0.0" + +color-space@^2.0.0, color-space@^2.0.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/color-space/-/color-space-2.3.1.tgz#1c13ca8ee017585807e65c76de3870cdc7c4fdfb" + integrity sha512-5DJdKYwoDji3ik/i0xSn+SiwXsfwr+1FEcCMUz2GS5speGCfGSbBMOLd84SDUBOuX8y4CvdFJmOBBJuC4wp7sQ== + commander@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" @@ -520,6 +555,11 @@ dlv@^1.1.3: resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== +earcut@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/earcut/-/earcut-3.0.1.tgz#f60b3f671c5657cca9d3e131c5527c5dde00ef38" + integrity sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" @@ -629,6 +669,20 @@ function-bind@^1.1.2: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +geotiff@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/geotiff/-/geotiff-2.1.3.tgz#993f40f2aa6aa65fb1e0451d86dd22ca8e66910c" + integrity sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA== + dependencies: + "@petamoriken/float16" "^3.4.7" + lerc "^3.0.0" + pako "^2.0.4" + parse-headers "^2.0.2" + quick-lru "^6.1.1" + web-worker "^1.2.0" + xml-utils "^1.0.2" + zstddec "^0.1.0" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" @@ -739,10 +793,10 @@ jquery-ujs@^1.2.2: resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== -leaflet@^1.9.4: - version "1.9.4" - resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d" - integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA== +lerc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lerc/-/lerc-3.0.0.tgz#36f36fbd4ba46f0abf4833799fff2e7d6865f5cb" + integrity sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww== lilconfig@^3.0.0, lilconfig@^3.1.3: version "3.1.3" @@ -828,11 +882,34 @@ object-hash@^3.0.0: resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== +ol@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/ol/-/ol-10.4.0.tgz#b073dd2b08c3cc31fece1c904a6711e0d289d6e4" + integrity sha512-gv3voS4wgej1WVvdCz2ZIBq3lPWy8agaf0094E79piz8IGQzHiOWPs2in1pdoPmuTNvcqGqyUFG3IbxNE6n08g== + dependencies: + "@types/rbush" "4.0.0" + color-rgba "^3.0.0" + color-space "^2.0.1" + earcut "^3.0.0" + geotiff "^2.1.3" + pbf "4.0.1" + rbush "^4.0.0" + package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== +pako@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + +parse-headers@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" + integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== + path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" @@ -851,6 +928,13 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +pbf@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pbf/-/pbf-4.0.1.tgz#ad9015e022b235dcdbe05fc468a9acadf483f0d4" + integrity sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA== + dependencies: + resolve-protobuf-schema "^2.1.0" + photoswipe@^5.4.4: version "5.4.4" resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.4.4.tgz#e045dc036453493188d5c8665b0e8f1000ac4d6e" @@ -929,11 +1013,33 @@ postcss@^8.4.47, postcss@^8.5.1: picocolors "^1.1.1" source-map-js "^1.2.1" +protocol-buffers-schema@^3.3.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" + integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-6.1.2.tgz#e9a90524108629be35287d0b864e7ad6ceb3659e" + integrity sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ== + +quickselect@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-3.0.0.tgz#a37fc953867d56f095a20ac71c6d27063d2de603" + integrity sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g== + +rbush@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/rbush/-/rbush-4.0.1.tgz#1f55afa64a978f71bf9e9a99bc14ff84f3cb0d6d" + integrity sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ== + dependencies: + quickselect "^3.0.0" + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" @@ -953,6 +1059,13 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +resolve-protobuf-schema@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758" + integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ== + dependencies: + protocol-buffers-schema "^3.3.1" + resolve@^1.1.7, resolve@^1.22.8: version "1.22.10" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz" @@ -1140,6 +1253,11 @@ util-deprecate@^1.0.2: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +web-worker@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.5.0.tgz#71b2b0fbcc4293e8f0aa4f6b8a3ffebff733dcc5" + integrity sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw== + which@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" @@ -1165,7 +1283,17 @@ wrap-ansi@^8.1.0: string-width "^5.0.1" strip-ansi "^7.0.1" +xml-utils@^1.0.2: + version "1.10.1" + resolved "https://registry.yarnpkg.com/xml-utils/-/xml-utils-1.10.1.tgz#fa0c9b38545760532d4cf89003f90c3b24e7200f" + integrity sha512-Dn6vJ1Z9v1tepSjvnCpwk5QqwIPcEFKdgnjqfYOABv1ngSofuAhtlugcUC3ehS1OHdgDWSG6C5mvj+Qm15udTQ== + yaml@^2.3.4: version "2.7.0" resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + +zstddec@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec" + integrity sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg== From daa0ceac3e9dac99755c950a26c3326e1bb02572 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 11:21:12 +0800 Subject: [PATCH 06/12] refactor: replace OpenLayers with Mapbox GL - Replace OpenLayers with Mapbox GL for map rendering. - Update CSS for map styling and control visibility. - Integrate weather art into map popups. - Add Mapbox token to credentials.yml.enc This commit replaces the existing OpenLayers map implementation with Mapbox GL. It also adjusts the styling and adds a weather art display to the map popup, which enhances the user experience. The necessary changes include modifying stylesheets, JavaScript controllers, view templates, and updating the credentials file. The motivation is to enhance map rendering performance and user experience. --- .../stylesheets/application.tailwind.css | 37 ++-- app/javascript/controllers/map_controller.js | 107 +++++----- app/views/cities/_map.html.erb | 28 +-- app/views/cities/show.html.erb | 2 +- config/credentials.yml.enc | 2 +- package.json | 3 +- yarn.lock | 188 ++++++++++++++++++ 7 files changed, 279 insertions(+), 88 deletions(-) diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css index f73b6ad..bb23c61 100644 --- a/app/assets/stylesheets/application.tailwind.css +++ b/app/assets/stylesheets/application.tailwind.css @@ -1,6 +1,5 @@ @import "photoswipe/dist/photoswipe.css"; -/*@import "leaflet/dist/leaflet.css";*/ -@import 'ol/ol.css'; +@import "mapbox-gl/dist/mapbox-gl.css"; @tailwind base; @tailwind components; @@ -45,28 +44,18 @@ transform-origin: center center; } -.map-container { - width: 100%; - height: 400px; - position: relative; -} +@layer components { + .mapboxgl-ctrl-logo, + .mapboxgl-ctrl-attrib { + @apply hidden !important; /* 隐藏版权信息 */ + } -/* 使弹出窗口在地图上可见 */ -.ol-overlay-container { - will-change: transform; - position: absolute; - transform: translate(-50%, -100%); - pointer-events: auto; -} + .mapboxgl-ctrl-zoom-in, + .mapboxgl-ctrl-zoom-out { + @apply bg-base-200 text-base-content hover:bg-base-300 p-2; + } -/* 添加一个小箭头 */ -.ol-overlay-container::after { - content: ''; - position: absolute; - top: 100%; - left: 50%; - margin-left: -8px; - border-width: 8px; - border-style: solid; - border-color: white transparent transparent transparent; + .mapboxgl-marker path { + @apply fill-current text-primary; + } } \ No newline at end of file diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js index 65dde67..5956afe 100644 --- a/app/javascript/controllers/map_controller.js +++ b/app/javascript/controllers/map_controller.js @@ -1,61 +1,72 @@ import { Controller } from "@hotwired/stimulus" -import Map from 'ol/Map' -import View from 'ol/View' -import TileLayer from 'ol/layer/Tile' -import OSM from 'ol/source/OSM' -import { fromLonLat } from 'ol/proj' -import Feature from 'ol/Feature' -import Point from 'ol/geom/Point' -import { Vector as VectorLayer } from 'ol/layer' -import { Vector as VectorSource } from 'ol/source' -import { Style, Icon } from 'ol/style' - +import mapboxgl from 'mapbox-gl' +import 'mapbox-gl/dist/mapbox-gl.css'; export default class extends Controller { static values = { latitude: Number, - longitude: Number + longitude: Number, + zoom: { type: Number, default: 6 }, + weatherArt: Object, + weatherArtUrl: String, + token: String } connect() { - this.initializeMap() + mapboxgl.accessToken = this.tokenValue + + this.map = new mapboxgl.Map({ + container: this.element, + style: 'mapbox://styles/mapbox/satellite-streets-v12', + // projection: 'globe', // 启用 3D 地球模式 + center: [this.longitudeValue, this.latitudeValue], + zoom: this.zoomValue + }); + + this.map.on('style.load', () => { + // 设置地球效果 + this.map.setFog({ + 'color': 'rgb(186, 210, 235)', + 'high-color': 'rgb(36, 92, 223)', + 'horizon-blend': 0.02 + }); + }); + + // 添加标记 + // const marker = new mapboxgl.Marker({ + // color: "#FF6B6B", + // scale: 1.2 + // }) + // .setLngLat([this.longitudeValue, this.latitudeValue]) + // .addTo(this.map); + + const marker = new mapboxgl.Marker() + .setLngLat([this.longitudeValue, this.latitudeValue]) + .addTo(this.map); + marker.getElement().addEventListener('click', () => { + this.showPopup(); + }); + + // 默认弹出窗口 + // this.showPopup(); + + // 添加缩放控件 + this.map.addControl(new mapboxgl.NavigationControl()); } - initializeMap() { - // 创建地图实例 - const map = new Map({ - target: this.element, - layers: [ - new TileLayer({ - source: new OSM() - }) - ], - view: new View({ - center: fromLonLat([this.longitudeValue, this.latitudeValue]), - zoom: 4 - }) - }) + showPopup() { + console.log("weatherArtValue: ", this.weatherArtValue) + const popupContent = ` +
+ ${this.weatherArtValue.description} +

${this.weatherArtValue.description}

+ 查看详情 +
+ `; - // 添加标记点 - const marker = new Feature({ - geometry: new Point(fromLonLat([this.longitudeValue, this.latitudeValue])) - }) - - const vectorSource = new VectorSource({ - features: [marker] - }) - - const vectorLayer = new VectorLayer({ - source: vectorSource, - style: new Style({ - image: new Icon({ - anchor: [0.5, 1], - src: '/marker-icon.svg', // 确保在public目录下有这个图标 - scale: 1.5 - }) - }) - }) - - map.addLayer(vectorLayer) + const popup = new mapboxgl.Popup() + .setLngLat([this.longitudeValue, this.latitudeValue]) + .setHTML(popupContent) + .addTo(this.map); } } \ No newline at end of file diff --git a/app/views/cities/_map.html.erb b/app/views/cities/_map.html.erb index 80a25da..ae6e00c 100644 --- a/app/views/cities/_map.html.erb +++ b/app/views/cities/_map.html.erb @@ -1,14 +1,16 @@ - -
-
-
- -
-
-
-
+ +
+
+ +
+
\ No newline at end of file diff --git a/app/views/cities/show.html.erb b/app/views/cities/show.html.erb index 9997de4..f0344a4 100644 --- a/app/views/cities/show.html.erb +++ b/app/views/cities/show.html.erb @@ -20,7 +20,7 @@ <% end %>
-
+
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 7f598a1..b1ee349 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -mNiwLK27M1vAWefYLzYIiQO0jBJqqZ1Yo0T1YOcldA3YrQo+AVm1vImv7Nbc96N3OK4f782Q3lNp4mTPl0zUFjiWy2QN/VdBMN9B2uxcUXJIASCJMwDlrc+e2evD6dqUb/hTj0q2Hi/uMVuRBI1O/bDvxxBQma/4MyPgwm6uwnP2g1yLfiBkgveU72yc4tHHmzXwEoxtJa3VkVKus+QWwCyWe/ZKvoDF2RplQm7RYVXgsbNhSdTVfoGvH9ExKRZvKjzrXutLtXe4YpHiiLSisCQBLYqF/XptG86U/uFldtCcbcB6XX2psyrQxHmhdxHnucMSx35rvh4XrjPMNKDpCiYuvJepjaJzgPDRkxjf4txIUHnIs3p6lVto+b92vq2OGKAq/nUwTpAB46WVz4CNCI0Z+QHkbSZ05a6cHAkzBY1Z4uiwu7gM6AFN/jYOdDo6GzxrP6h/Msp3wmOgKiM8Ts1kysXNdJ6E8X8GHPNKfHLnyVnoVrJbY1GGv6oXu4WkIxAK5v6Mi10J0Sd6QQJuZYv2SKNCjXUtxrm4NFWd9DY3BMvF3TSDRU17riruPyJHCYKSqCQHobblC7Sr1lwcN3+CfOJqGXR66D0OH6DNiHwobxsyVSHzSePl5bVi9a44V+GtzDJ0fZQYOpVnzWOsMj++K89dz2qtruB7BSOQe9YAXGk+dk//XqUebz4fqYUiW79aj4M1DyFquapbcn+WpzmXFGuVORM2ZuQjUr0Glev8JMq95R4JIZkMDwm5/q7YUbqEb25yMdhs5c48vmCIy3itliieWN9kYZO6pqIsP73KXms2Xi5nJ+FcaFQ9T24zs/S9JcA1a2KUl6n8kYBcVYBdNBvE+9ab+eJ3RUp3YMOErmMHAtTsUXQCZLmXpRheaaOkupS/XB+M+jYgYLI6+sv86ISZsYz472iT9UMCn5F/X8lNIwDClPoJP2kafLDq6hMlo76Oa/aVj+miy7tJIWFgVvMJ3VwPf526MMZgNShF6tZ+Z7PA0pr+9WuoZSyQ0Ai9lql/Tk+r2ZUPT6Z28qjGr9ln2jbb509ETwhSxnHjINskNyLmfFWUcELy+BEmOSn9GQgay0PoFkUrYpidpXD9Tw2IpEpklyIGhr4HFHv4bQ5uBWjPrIGm3R8fjATA0xB+Wg9SDPSHd+QclAzcL6Po8UlLIZfROjReg7ZJn5+8x0rf7UidNXTfy+JM0GmUR9aUh110RKE0u1WjdhhzdVDiQH2m58CZf3TsP3z4o8SLWfXAftqU8p3sBXPOV+L5RyWrUMzvWmDFzL0RmP854fOILsq76QoI4PULPHD7AIGaCHxuM+qmNQvYhFBMDCaHEeLP4ARX2NmUxMCT40T7bWEFbrAJ14Kd8L/EAjYVsGrBTWGZSvB3aZcZnP3cy4kC9XWvfAksstm9dPSqbxH7d8POb5KJBhSnfm5UWczc24s/6TC9UON921Shz3e9tk7bbBSBNIA/pHWmoZ8vFS3Cegihnt9TnALlWDBrGm7arYr3oQJHqkQ4KPEh36EwZDw/+YDaXLhN2yR8hL7nGPsewcwKdYEU4bGjTpI6xtjs0hBgwuMwTNWUsEm+bijt2feTxzJgUyT9ywW7aSsDfZi88J06LMJfpeAUIjhTjB9Coohk33lrpmsrD5Hb3RaObELlrpG6RkCHlmTxMTjTonS9ni+7dXUYJFYYvtLuyFmhSxEEk7bCrCMVkOWN5tTs0y0J22bxT/FrGLn/GKKlK/FVAeoGN8ZvEOCmIizEQ8k6wgAbuLDaTxV1irLLOeaY3JtjwVxBQHfNuCgxKXxd0zcUqv7D/sgMC0zafcqshG8VaXfGvFexWVEnZjPiZ6aHzeNMhdTPsf9D0p8R3PFgcvC2rSLYNbj2MXnvGqkrtQCre0CiNVxflVvOHbJjugdp5LqAjf6bssCsrZKifaRTPeg=--B6BEVJN27qT5bDUL--B4LzjOJ7SlzEKibq4VSVmg== \ No newline at end of file +VUzo0X7975miXMnnoTVXWA5260MuOUxsdsVtC079F3Tquo6Z8Re/EBiIM4hUKHX91D5CsVmPquvwj/AxW9JQxKtSUWYepyildV1B7nYAceqefvrqU+98qYvcKFbwTYXmw4kZD8lOgElcGEUNq6LgCHHXfrtEDHk0Sl6k8Pf5Pgc/SheFZwtK9FE4MGQ6y+hNaeMb7ct2E1zKKqyJKB4Lzp/Fgo7VJuIdEy8Ck23UweaJbSJ08yfS7XrmG1i2ak3fH8FCDxUrofIvyzLw27g8th071JhqiiS9fxJezH+JdaACnKao3bi5fSDL37R0nSBLOYV/Pm63Q3/Nmr560neNgErYtbIZPLETqAZTIK21yvrdaQAXlBEy6St8gdxpQNPELIVJAZ0fAaTfoFdS4/WlMt9oz9Osey9e+eWmDFhpQ/lAj+5oU0t81ioO4S+DBob6rgsAltGK/wa3GdXO2j7ACVJBv0znX3Mran8QiqGYl85+UNxLDH8fIkDyKiug8naWSxQYZIrFOHY5DG4M/IrI5u0zKiks+6kRDN7pRm1WDuW6EpEG5fDv4JRaRp41vS6ezCzXYDPIrGMDtksoJp64dDXdtD6w8NxSSXVhdBzcpZWDXzjpo/rmBM1t8WM46FsAKBrQNTi26B7q1J3rXb3zw2WmzzvVvEuKqVpKbc+d8ND8XUTBYM4d4bPrVSUtC0twMPwAnR0P3PNmV7EHCgfnOuKqDgdqpREpePPzHRpXTS1AxSehLRLO7te8SPN+NpD8Yz+drGmoV1Tx7MuymzK45ILRoBgKaBFkCVZayxhGxDRPDjtYLkQ3ZTA3MUwRhyDVQX47J3wAL5zVRmPiWobXoWEtFalW61bxvD9n9Mr1WP59oYlU4xBxb+0Yu+mvysLBMW9PIAviUknKzmkjrUu4ywqV2QrWlT2tZtM/gWDt5tvLWKDPB89SlzZ55Y6auGFFbYv7HUICo3lk2JFXNfebJF0oXEcmYq3szb80IVIWgp4pdNXOYyh9Pu7g7dZnGTPyiITmhHge/9mj7FoOxOGDsvMedefN86IPN/wCEg/fsJBzhJ0YovrwaTn5+GN9NV92TqwK3LWGkQZ4aZnOAJxIO6b4E3cebPfYjnps39pI0sx9aIN5b0XbjI6d50iLh5jz+2v0B6nDzVEyCiG6zpXXi27ZehO921G0dEQrCuWzVikquUnJbJGVf2tdwKjRDCJWhwPIkhP9YeY6uzQTjGtqDrd12VoEReFMgn1K/PjpJ0s55slbc9+BjOCCuoDTXDy6NC4n3/bsNNKPjt5x0pXkTDh90ug7IBdLweXskhrR86FBwZuf8GGZfz4ZbrVlzhIIPiG/UbkzhaVJDcJrawHj2ipcToXIoDg4Wt3+ZTGC+Kjxk5DzLJWxtIReU34xU9Fv+jndWnu4GJ4+scbwS3mUhBXdcMufeRVh1T45/PSnkwLJRVMv5c5f3CP16PkDORrmuIC+ws3Dz5lCEu1/tuaFabtY3sF6jUY6h/EIUxMllrGwzH9PI/dLu3UizRdtBR85iW03wvAxmLAABmXuRhgyca50uJ65qnFhzLRtQ3JlAIRxZT62YkawqAlUBgstuUgbMLxsCJXLhMmBCO+GuqvkF9DkPVO5dVkNj9V/dnuzqhwzbkDHGd5hwMrXMIYVii5e4q+D5eiTuSfGo/kdHIXfrGrUpUkmbkRrTsnNDUnDzDCk5CWUpuiIj7FSUY8L8Yokli04HRCCn++niPVHZM/RZIZQXAqt61YpfgPr80ut3tmW1aRsLMnkOTjL0KTW3xDv/hxjKuTZj3JqKOFISdI5zihYRwHDBTiClWEnGqkuqSS9fl56A/m17yviId15rzvBIdcLRziQnnpgUacX0lcMdv/A48SCJwAKhTzc2f9Jt8IbFW2OH3+o6QPQC3mysl9ESatJ0saZ7VFQvGSRqG+iRY7OlX2d4LssBke0KBkU4EnZOPXQ24A72m1nooWiz83DBe62d9A/XoKkX9OFh0XQypjTTu0KHqdLsEAqFqISyCz7Vxjd3pdVjaC1lJ/4hqFn3M8RJNTRefXNzgkG1ZpEOA7IGsal+rUaEDSXC8by--JNyAqrs+Fsmpu043--vwHHQzq3PsM/qcTSemHHAg== \ No newline at end of file diff --git a/package.json b/package.json index 10c2467..0644ede 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "esbuild": "^0.24.2" }, "scripts": { - "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets --loader:.woff=file --loader:.woff2=file --loader:.ttf=file --loader:.eot=file --loader:.png=file --asset-names=[name]-[hash].digested --chunk-names=[name]-[hash].digested", + "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets --loader:.woff=file --loader:.woff2=file --loader:.ttf=file --loader:.eot=file --asset-names=[name]-[hash]", "build:css": "tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css && sass ./app/assets/stylesheets/active_admin.scss:./app/assets/builds/active_admin.css --no-source-map --load-path=node_modules" }, "dependencies": { @@ -18,6 +18,7 @@ "autoprefixer": "^10.4.20", "jquery": "^3.7.1", "jquery-ui": "^1.14.1", + "mapbox-gl": "^3.10.0", "ol": "^10.4.0", "photoswipe": "^5.4.4", "postcss": "^8.5.1", diff --git a/yarn.lock b/yarn.lock index fe11816..4a97adb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -213,6 +213,43 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@mapbox/jsonlint-lines-primitives@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" + integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ== + +"@mapbox/mapbox-gl-supported@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-3.0.0.tgz#bebd3d5da3c1fd988011bb79718a39f63f5e16ac" + integrity sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg== + +"@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" + integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ== + +"@mapbox/tiny-sdf@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz#9a1d33e5018093e88f6a4df2343e886056287282" + integrity sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA== + +"@mapbox/unitbezier@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01" + integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw== + +"@mapbox/vector-tile@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz#d3a74c90402d06e89ec66de49ec817ff53409666" + integrity sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw== + dependencies: + "@mapbox/point-geometry" "~0.1.0" + +"@mapbox/whoots-js@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe" + integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" @@ -338,11 +375,49 @@ resolved "https://registry.npmjs.org/@rails/actioncable/-/actioncable-7.2.201.tgz" integrity sha512-wsTdWoZ5EfG5k3t7ORdyQF0ZmDEgN4aVPCanHAiNEwCROqibSZMXXmCbH7IDJUVri4FOeAVwwbPINI7HVHPKBw== +"@types/geojson-vt@^3.2.5": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/geojson-vt/-/geojson-vt-3.2.5.tgz#b6c356874991d9ab4207533476dfbcdb21e38408" + integrity sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g== + dependencies: + "@types/geojson" "*" + +"@types/geojson@*", "@types/geojson@^7946.0.16": + version "7946.0.16" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" + integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + +"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz#0ef017b75eedce02ff6243b4189210e2e6d5e56d" + integrity sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA== + +"@types/mapbox__vector-tile@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz#ad757441ef1d34628d9e098afd9c91423c1f8734" + integrity sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg== + dependencies: + "@types/geojson" "*" + "@types/mapbox__point-geometry" "*" + "@types/pbf" "*" + +"@types/pbf@*", "@types/pbf@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.5.tgz#a9495a58d8c75be4ffe9a0bd749a307715c07404" + integrity sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA== + "@types/rbush@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/rbush/-/rbush-4.0.0.tgz#b327bf54952e9c924ea6702c36904c2ce1d47f35" integrity sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ== +"@types/supercluster@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/supercluster/-/supercluster-7.1.3.tgz#1a1bc2401b09174d9c9e44124931ec7874a72b27" + integrity sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA== + dependencies: + "@types/geojson" "*" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -439,6 +514,11 @@ caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz" integrity sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A== +cheap-ruler@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cheap-ruler/-/cheap-ruler-4.0.0.tgz#bdc984de7e0e3f748bdfd2dbe23ec6b9dc820a09" + integrity sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw== + chokidar@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" @@ -520,6 +600,11 @@ css-selector-tokenizer@^0.8: cssesc "^3.0.0" fastparse "^1.1.2" +csscolorparser@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b" + integrity sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" @@ -669,6 +754,11 @@ function-bind@^1.1.2: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +geojson-vt@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/geojson-vt/-/geojson-vt-4.0.2.tgz#1162f6c7d61a0ba305b1030621e6e111f847828a" + integrity sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A== + geotiff@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/geotiff/-/geotiff-2.1.3.tgz#993f40f2aa6aa65fb1e0451d86dd22ca8e66910c" @@ -683,6 +773,11 @@ geotiff@^2.1.3: xml-utils "^1.0.2" zstddec "^0.1.0" +gl-matrix@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9" + integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA== + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" @@ -709,6 +804,11 @@ glob@^10.3.10: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +grid-index@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/grid-index/-/grid-index-1.1.0.tgz#97f8221edec1026c8377b86446a7c71e79522ea7" + integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA== + hasown@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" @@ -716,6 +816,11 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +ieee754@^1.1.12: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + immutable@^5.0.2: version "5.0.3" resolved "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz" @@ -793,6 +898,11 @@ jquery-ujs@^1.2.2: resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== +kdbush@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-4.0.2.tgz#2f7b7246328b4657dd122b6c7f025fbc2c868e39" + integrity sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA== + lerc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lerc/-/lerc-3.0.0.tgz#36f36fbd4ba46f0abf4833799fff2e7d6865f5cb" @@ -813,6 +923,40 @@ lru-cache@^10.2.0: resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +mapbox-gl@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/mapbox-gl/-/mapbox-gl-3.10.0.tgz#9104a4e2f75fe2cf2a8ba015397be59f5179ba8c" + integrity sha512-YnQxjlthuv/tidcxGYU2C8nRDVXMlAHa3qFhuOJeX4AfRP72OMRBf9ApL+M+k5VWcAXi2fcNOUVgphknjLumjA== + dependencies: + "@mapbox/jsonlint-lines-primitives" "^2.0.2" + "@mapbox/mapbox-gl-supported" "^3.0.0" + "@mapbox/point-geometry" "^0.1.0" + "@mapbox/tiny-sdf" "^2.0.6" + "@mapbox/unitbezier" "^0.0.1" + "@mapbox/vector-tile" "^1.3.1" + "@mapbox/whoots-js" "^3.1.0" + "@types/geojson" "^7946.0.16" + "@types/geojson-vt" "^3.2.5" + "@types/mapbox__point-geometry" "^0.1.4" + "@types/mapbox__vector-tile" "^1.3.4" + "@types/pbf" "^3.0.5" + "@types/supercluster" "^7.1.3" + cheap-ruler "^4.0.0" + csscolorparser "~1.0.3" + earcut "^3.0.0" + geojson-vt "^4.0.2" + gl-matrix "^3.4.3" + grid-index "^1.1.0" + kdbush "^4.0.2" + murmurhash-js "^1.0.0" + pbf "^3.2.1" + potpack "^2.0.0" + quickselect "^3.0.0" + serialize-to-js "^3.1.2" + supercluster "^8.0.1" + tinyqueue "^3.0.0" + vt-pbf "^3.1.3" + merge2@^1.3.0: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" @@ -838,6 +982,11 @@ minimatch@^9.0.4: resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +murmurhash-js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51" + integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw== + mz@^2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" @@ -935,6 +1084,14 @@ pbf@4.0.1: dependencies: resolve-protobuf-schema "^2.1.0" +pbf@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.3.0.tgz#1790f3d99118333cc7f498de816028a346ef367f" + integrity sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q== + dependencies: + ieee754 "^1.1.12" + resolve-protobuf-schema "^2.1.0" + photoswipe@^5.4.4: version "5.4.4" resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.4.4.tgz#e045dc036453493188d5c8665b0e8f1000ac4d6e" @@ -1013,6 +1170,11 @@ postcss@^8.4.47, postcss@^8.5.1: picocolors "^1.1.1" source-map-js "^1.2.1" +potpack@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/potpack/-/potpack-2.0.0.tgz#61f4dd2dc4b3d5e996e3698c0ec9426d0e169104" + integrity sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw== + protocol-buffers-schema@^3.3.1: version "3.6.0" resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" @@ -1098,6 +1260,11 @@ sass@^1.83.4: optionalDependencies: "@parcel/watcher" "^2.4.1" +serialize-to-js@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/serialize-to-js/-/serialize-to-js-3.1.2.tgz#844b8a1c2d72412f68ea30da55090b3fc8e95790" + integrity sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -1181,6 +1348,13 @@ sucrase@^3.35.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +supercluster@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-8.0.1.tgz#9946ba123538e9e9ab15de472531f604e7372df5" + integrity sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ== + dependencies: + kdbush "^4.0.2" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" @@ -1228,6 +1402,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +tinyqueue@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-3.0.0.tgz#101ea761ccc81f979e29200929e78f1556e3661e" + integrity sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -1253,6 +1432,15 @@ util-deprecate@^1.0.2: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +vt-pbf@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" + integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA== + dependencies: + "@mapbox/point-geometry" "0.1.0" + "@mapbox/vector-tile" "^1.3.1" + pbf "^3.2.1" + web-worker@^1.2.0: version "1.5.0" resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.5.0.tgz#71b2b0fbcc4293e8f0aa4f6b8a3ffebff733dcc5" From b6635e5a517dace09fa9187f8950af1b6d517ee5 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 11:48:40 +0800 Subject: [PATCH 07/12] fix: fix map marker click and handle null values - Comment out marker click listener to fix a bug - Update _map.html.erb to handle null values in the city - Improve error handling This commit addresses a bug where clicking the map marker caused unexpected behavior. The changes involve commenting out the click listener to fix the issue, and updating the view template to handle potential null values in the city object to prevent unexpected behavior. --- app/javascript/controllers/map_controller.js | 6 +++--- app/views/cities/_map.html.erb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js index 5956afe..cca4004 100644 --- a/app/javascript/controllers/map_controller.js +++ b/app/javascript/controllers/map_controller.js @@ -43,9 +43,9 @@ export default class extends Controller { const marker = new mapboxgl.Marker() .setLngLat([this.longitudeValue, this.latitudeValue]) .addTo(this.map); - marker.getElement().addEventListener('click', () => { - this.showPopup(); - }); + // marker.getElement().addEventListener('click', () => { + // this.showPopup(); + // }); // 默认弹出窗口 // this.showPopup(); diff --git a/app/views/cities/_map.html.erb b/app/views/cities/_map.html.erb index ae6e00c..2f5b375 100644 --- a/app/views/cities/_map.html.erb +++ b/app/views/cities/_map.html.erb @@ -8,7 +8,7 @@ data-map-longitude-value="<%= @city.longitude %>" data-map-token-value="<%= Rails.application.credentials.dig(:mapbox, :token) %>" data-map-weather-art-value="<%= @city.latest_weather_art.to_json %>" - data-map-weather-art-url-value="<%= rails_blob_url(@city.latest_weather_art.webp_image.processed ) %>" + data-map-weather-art-url-value="<%= rails_blob_url(@city&.latest_weather_art&.webp_image&.processed ) if @city&.latest_weather_art&.image&.attached %>" class="h-[500px] w-full rounded-b-2xl z-10" style="touch-action: none" >
From 968efb5492619f0f925f3c7ed7ac8d2cc83fb896 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 11:50:36 +0800 Subject: [PATCH 08/12] fix: update UI of the show page - Refactor to remove the link's padding-top. - The navigation back button should now work as expected. - No known side effects. --- app/views/cities/show.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/cities/show.html.erb b/app/views/cities/show.html.erb index f0344a4..02db839 100644 --- a/app/views/cities/show.html.erb +++ b/app/views/cities/show.html.erb @@ -12,7 +12,7 @@
<%= link_to cities_path, - class: "inline-flex items-center btn btn-ghost gap-2 pt-6 mb-8" do %> + class: "inline-flex items-center btn btn-ghost gap-2 " do %> From df074a81a8372b177f16dce51b545b167fe1be4f Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 12:01:19 +0800 Subject: [PATCH 09/12] fix: access city attribute correctly in WeatherArt model - Modified the formatted_time method to use 'self' when accessing the city attribute - This change fixes a bug where the city attribute was not being accessed correctly, ensuring proper timezone information retrieval. --- app/models/weather_art.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/weather_art.rb b/app/models/weather_art.rb index 6c37484..01b3422 100644 --- a/app/models/weather_art.rb +++ b/app/models/weather_art.rb @@ -70,7 +70,7 @@ class WeatherArt < ApplicationRecord def formatted_time(type = :date, use_local_timezone = false) # 获取时区 - timezone_info = city&.country&.timezones.present? ? + timezone_info = self.city&.country&.timezones.present? ? eval(self.city.country.timezones).first : { "zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00" } From 936db76437511cf553e7ebbd7bdd6fdae882d792 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 12:20:24 +0800 Subject: [PATCH 10/12] refactor: Improve weather art generation process - Add formatted_current_time method to City model. - Modify BatchGenerateWeatherArtsWorker to check local time. - Update Sidekiq scheduler to run every hour. This commit improves the weather art generation by considering local time for image generation and increasing the frequency of batch processing for testing purposes. The new method provides formatted time information for each city. This change ensures that images are generated during daylight hours. --- app/models/city.rb | 39 +++++++++-- .../batch_generate_weather_arts_worker.rb | 65 +++++++++++++------ config/sidekiq_scheduler.yml | 2 +- 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/app/models/city.rb b/app/models/city.rb index ee590a3..18fb0a7 100644 --- a/app/models/city.rb +++ b/app/models/city.rb @@ -109,7 +109,7 @@ class City < ApplicationRecord # 定义 latest_weather_art 关联 has_one :latest_weather_art, -> { order(weather_date: :desc) }, - class_name: 'WeatherArt' + class_name: "WeatherArt" # 包含最新天气艺术的 scope scope :with_latest_weather_art, -> { @@ -124,14 +124,14 @@ class City < ApplicationRecord # 按最新天气更新时间排序 scope :order_by_latest_weather, -> { joins(:weather_arts) - .group('cities.id') - .order('MAX(weather_arts.weather_date) DESC') + .group("cities.id") + .order("MAX(weather_arts.weather_date) DESC") } # 获取最近24小时内更新过天气的城市 scope :recently_updated, -> { joins(:weather_arts) - .where('weather_arts.weather_date > ?', 24.hours.ago) + .where("weather_arts.weather_date > ?", 24.hours.ago) .distinct } @@ -213,5 +213,36 @@ class City < ApplicationRecord self.id) .count end + end + + def formatted_current_time(type = :date, use_local_timezone = true) + # 获取时区 + timezone_info = self.country&.timezones.present? ? + eval(self.country.timezones).first : + { "zoneName" => "UTC", "gmtOffsetName" => "UTC+00:00" } + + # 设置时区对象 + time_zone = ActiveSupport::TimeZone[timezone_info["zoneName"]] || + ActiveSupport::TimeZone["UTC"] + time = Time.current + + case type + when :date + # 格式化日期 + time.strftime("%B %d, %Y") + when :time + use_local_timezone ? + "#{time.in_time_zone(time_zone).strftime('%H:%M')} #{timezone_info['gmtOffsetName']}" : + "#{time.utc.strftime('%H:%M')} UTC" + when :all + # 返回日期 + 时间 + UTC 信息 + date = time.strftime("%B %d, %Y") + time = use_local_timezone ? + updated_at.in_time_zone(time_zone).strftime("%H:%M") + " #{timezone_info['gmtOffsetName']}" : + updated_at.utc.strftime("%H:%M") + " UTC" + "#{date} #{time}" + else + "Unknown #{type}" end + end end diff --git a/app/workers/batch_generate_weather_arts_worker.rb b/app/workers/batch_generate_weather_arts_worker.rb index 2d29c0e..4daa9a5 100644 --- a/app/workers/batch_generate_weather_arts_worker.rb +++ b/app/workers/batch_generate_weather_arts_worker.rb @@ -2,8 +2,8 @@ class BatchGenerateWeatherArtsWorker include Sidekiq::Worker GENERATION_INTERVAL = 36.hours - MAX_DURATION = 50.minutes - SLEEP_DURATION = 120.seconds + MAX_DURATION = 5.minutes + SLEEP_DURATION = 10.seconds DAILY_GENERATION_LIMIT = 50 # 每日生成图片上限 def perform(*args) @@ -11,10 +11,26 @@ class BatchGenerateWeatherArtsWorker remaining_slots = calculate_remaining_slots return if remaining_slots <= 0 - # 获取符合条件的城市并处理 cities_to_process = select_cities(remaining_slots)&.shuffle print_cities_list(cities_to_process, start_time) - process_cities(cities_to_process, start_time) + + skipped_cities = [] + processed_cities = [] + + cities_to_process.each do |city| + if within_sunrise_time?(city) + Rails.logger.info "Generating weather art for #{city.name}" + GenerateWeatherArtWorker.perform_async(city.id) + processed_cities << city.name + else + Rails.logger.info "Skipping #{city.name} due to local time not being within sunrise hours." + skipped_cities << city.name + end + sleep SLEEP_DURATION + break if Time.current - start_time > MAX_DURATION + end + + print_summary(processed_cities, skipped_cities) end private @@ -25,23 +41,42 @@ class BatchGenerateWeatherArtsWorker .where.not(image_attachment: nil) .count - [ DAILY_GENERATION_LIMIT - today_generations, 0 ].max + [DAILY_GENERATION_LIMIT - today_generations, 0].max end def print_cities_list(cities, start_time) Rails.logger.info "Generate city task start at: #{start_time}" Rails.logger.info "Generate city list: " Rails.logger.info "======================================" - Rails.logger.info "ID\tRegion\tCountry\tState\tName" + Rails.logger.info "ID\tRegion\tCountry\tState\tName\tLocalTime" cities.each do |city| - Rails.logger.info "#{city.id}\t#{city&.country&.region&.name}\t#{city&.country&.name}\t#{city&.state&.name}\t#{city&.name}" + Rails.logger.info "#{city.id}\t#{city.country&.region&.name}\t#{city.country&.name}\t#{city.state&.name}\t#{city.name}\t#{city.formatted_current_time(:all)}" end end + def within_sunrise_time?(city) + local_time = get_local_time(city) + local_time.hour >= 8 && local_time.hour <= 18 + end + + def get_local_time(city) + return Time.current unless city + timezone_info = city.country&.timezones&.first + return Time.current unless timezone_info + + timezone = ActiveSupport::TimeZone["zoneName"] || + ActiveSupport::TimeZone["UTC"] + Time.current.in_time_zone(timezone) + end + + def print_summary(processed_cities, skipped_cities) + Rails.logger.info "Processed cities: #{processed_cities.join(', ')}" + Rails.logger.info "Skipped cities: #{skipped_cities.join(', ')}" + end + def select_cities(limit) cutoff_time = Time.current - GENERATION_INTERVAL - # 基础查询:排除最近生成过的城市 base_query = City .joins("LEFT JOIN ( SELECT city_id, MAX(created_at) as last_generation_time @@ -50,7 +85,6 @@ class BatchGenerateWeatherArtsWorker ) latest_arts ON cities.id = latest_arts.city_id") .where("latest_arts.last_generation_time IS NULL OR latest_arts.last_generation_time < ?", cutoff_time) - # 优先选择活跃城市 active_cities = base_query .where(active: true) .order(:priority) @@ -60,7 +94,6 @@ class BatchGenerateWeatherArtsWorker remaining_slots = limit - active_cities.size if remaining_slots > 0 - # 如果还有剩余名额,从其他城市中随机选择 other_cities = base_query .where.not(id: active_cities.map(&:id)) .order("RANDOM()") @@ -72,14 +105,4 @@ class BatchGenerateWeatherArtsWorker active_cities end end - - def process_cities(cities, start_time) - cities.each do |city| - break if Time.current - start_time > MAX_DURATION - - Rails.logger.info "Generating weather art for #{city.name}" - GenerateWeatherArtWorker.perform_async(city.id) - sleep SLEEP_DURATION - end - end -end +end \ No newline at end of file diff --git a/config/sidekiq_scheduler.yml b/config/sidekiq_scheduler.yml index 6963062..8914b3f 100644 --- a/config/sidekiq_scheduler.yml +++ b/config/sidekiq_scheduler.yml @@ -1,5 +1,5 @@ batch_generate_weather: - cron: '0 8,18 * * *' + cron: '0 */1 * * *' class: BatchGenerateWeatherArtsWorker description: "Batch Generate weather arts" enabled: true From 20427327870039449f86038027e61b3957f7eb4a Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 12:30:48 +0800 Subject: [PATCH 11/12] feat: improve worker logging - Added current time logging to the worker to help debug and analyze processing times - Cleaned up assets initializer file --- app/workers/batch_generate_weather_arts_worker.rb | 13 +++++++------ config/initializers/assets.rb | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/workers/batch_generate_weather_arts_worker.rb b/app/workers/batch_generate_weather_arts_worker.rb index 4daa9a5..ff18c66 100644 --- a/app/workers/batch_generate_weather_arts_worker.rb +++ b/app/workers/batch_generate_weather_arts_worker.rb @@ -19,11 +19,11 @@ class BatchGenerateWeatherArtsWorker cities_to_process.each do |city| if within_sunrise_time?(city) - Rails.logger.info "Generating weather art for #{city.name}" + Rails.logger.info "Generating weather art for #{city.name} (time: [#{city.formatted_current_time(:all)}])" GenerateWeatherArtWorker.perform_async(city.id) processed_cities << city.name else - Rails.logger.info "Skipping #{city.name} due to local time not being within sunrise hours." + Rails.logger.info "Skipping #{city.name} (time: [#{city.formatted_current_time(:all)}]) due to local time not being within sunrise hours." skipped_cities << city.name end sleep SLEEP_DURATION @@ -41,7 +41,7 @@ class BatchGenerateWeatherArtsWorker .where.not(image_attachment: nil) .count - [DAILY_GENERATION_LIMIT - today_generations, 0].max + [ DAILY_GENERATION_LIMIT - today_generations, 0 ].max end def print_cities_list(cities, start_time) @@ -56,15 +56,16 @@ class BatchGenerateWeatherArtsWorker def within_sunrise_time?(city) local_time = get_local_time(city) + Rails.logger.info "#{city.name} now hour: #{local_time.hour}" local_time.hour >= 8 && local_time.hour <= 18 end def get_local_time(city) return Time.current unless city - timezone_info = city.country&.timezones&.first + timezone_info = eval(city.country.timezones).first return Time.current unless timezone_info - timezone = ActiveSupport::TimeZone["zoneName"] || + timezone = ActiveSupport::TimeZone[timezone_info["zoneName"]] || ActiveSupport::TimeZone["UTC"] Time.current.in_time_zone(timezone) end @@ -105,4 +106,4 @@ class BatchGenerateWeatherArtsWorker active_cities end end -end \ No newline at end of file +end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 42f11c9..270956e 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -10,4 +10,3 @@ Rails.application.config.assets.version = "1.0" # Rails.application.config.assets.precompile += %w( *.png *.jpg *.jpeg *.gif ) # Rails.application.config.assets.paths << Rails.root.join("app/assets/builds") # Rails.application.config.assets.precompile += %w( *.png *.jpg *.jpeg *.gif ) - From 299107b988def16fbdbdf421c901cfdddcc98470 Mon Sep 17 00:00:00 2001 From: songtianlun Date: Sat, 15 Feb 2025 12:32:04 +0800 Subject: [PATCH 12/12] feat: modify logging level of batch job worker The logging level of the batch job worker has been changed from info to debug. The change affects how the batch job worker outputs information about the cities it is processing. - Original behavior: The batch job worker logs cities' information at the info level. - New behavior: The batch job worker logs cities' information at the debug level. The motivation behind this change is to reduce the noise in the logs. The new behavior will only log cities' information that is relevant for debugging purposes. --- app/workers/batch_generate_weather_arts_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/batch_generate_weather_arts_worker.rb b/app/workers/batch_generate_weather_arts_worker.rb index ff18c66..8a285a6 100644 --- a/app/workers/batch_generate_weather_arts_worker.rb +++ b/app/workers/batch_generate_weather_arts_worker.rb @@ -56,7 +56,7 @@ class BatchGenerateWeatherArtsWorker def within_sunrise_time?(city) local_time = get_local_time(city) - Rails.logger.info "#{city.name} now hour: #{local_time.hour}" + Rails.logger.debug "#{city.name} now hour: #{local_time.hour}" local_time.hour >= 8 && local_time.hour <= 18 end