git-fit 0.7.9 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/lib/git-fit.rb +44 -42
  3. data/lib/git_fit/cli/export.rb +8 -6
  4. data/lib/git_fit/cli/geo_cli.rb +10 -8
  5. data/lib/git_fit/cli/gh_cli.rb +24 -22
  6. data/lib/git_fit/cli/import_cli.rb +40 -38
  7. data/lib/git_fit/cli/install_cli.rb +10 -8
  8. data/lib/git_fit/cli/sync.rb +9 -7
  9. data/lib/git_fit/cli.rb +29 -27
  10. data/lib/git_fit/config.rb +42 -28
  11. data/lib/git_fit/config_template.rb +2 -0
  12. data/lib/git_fit/db/activity.rb +2 -0
  13. data/lib/git_fit/db/cli.rb +11 -9
  14. data/lib/git_fit/db/connection.rb +8 -6
  15. data/lib/git_fit/db/rebuild.rb +5 -3
  16. data/lib/git_fit/export/defaults.rb +14 -12
  17. data/lib/git_fit/export/json.rb +5 -3
  18. data/lib/git_fit/export.rb +4 -2
  19. data/lib/git_fit/fit/decoder.rb +9 -7
  20. data/lib/git_fit/fit.rb +3 -1
  21. data/lib/git_fit/geo/amap_client.rb +15 -13
  22. data/lib/git_fit/geo/coord_transform.rb +2 -0
  23. data/lib/git_fit/geo/division_detector.rb +31 -26
  24. data/lib/git_fit/geo/nominatim_client.rb +14 -12
  25. data/lib/git_fit/geo/polyline.rb +4 -2
  26. data/lib/git_fit/geo/reverse_geocode.rb +11 -9
  27. data/lib/git_fit/geo.rb +8 -6
  28. data/lib/git_fit/import/apple_health.rb +84 -82
  29. data/lib/git_fit/import/local_file.rb +45 -42
  30. data/lib/git_fit/import.rb +4 -2
  31. data/lib/git_fit/install/actions.rb +15 -13
  32. data/lib/git_fit/parser/base.rb +12 -10
  33. data/lib/git_fit/parser/fit.rb +11 -9
  34. data/lib/git_fit/parser/gpx.rb +17 -15
  35. data/lib/git_fit/parser/tcx.rb +22 -20
  36. data/lib/git_fit/parser.rb +6 -4
  37. data/lib/git_fit/privacy/polyline_filter.rb +6 -4
  38. data/lib/git_fit/source/base.rb +25 -23
  39. data/lib/git_fit/source/tally.rb +9 -7
  40. data/lib/git_fit/source.rb +4 -2
  41. data/lib/git_fit/sport_mapper.rb +23 -21
  42. data/lib/git_fit/sync/base.rb +29 -27
  43. data/lib/git_fit/sync/garmin.rb +5 -3
  44. data/lib/git_fit/sync/garmin_base.rb +145 -143
  45. data/lib/git_fit/sync/garmin_base_di.rb +42 -40
  46. data/lib/git_fit/sync/garmin_cn.rb +8 -6
  47. data/lib/git_fit/sync/igpsport.rb +43 -41
  48. data/lib/git_fit/sync/keep.rb +98 -96
  49. data/lib/git_fit/sync/lorem.rb +31 -29
  50. data/lib/git_fit/sync/runner.rb +14 -11
  51. data/lib/git_fit/sync/strava.rb +61 -52
  52. data/lib/git_fit/sync/xingzhe.rb +52 -50
  53. data/lib/git_fit/sync/xoss.rb +68 -66
  54. data/lib/git_fit/timezone/resolver.rb +5 -3
  55. data/lib/git_fit/util/tally.rb +10 -8
  56. data/lib/git_fit/util.rb +2 -0
  57. data/lib/git_fit/version.rb +3 -1
  58. metadata +1 -1
data/lib/git_fit/cli.rb CHANGED
@@ -1,36 +1,38 @@
1
- require "thor"
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
2
4
 
3
5
  module GitFit
4
6
  class CLI < Thor
5
- package_name "git-fit"
7
+ package_name 'git-fit'
6
8
  include Cli::Sync
7
9
 
8
- class_option :config, type: :string, desc: "Config file path", aliases: "-c"
10
+ class_option :config, type: :string, desc: 'Config file path', aliases: '-c'
9
11
 
10
12
  def self.exit_on_failure?
11
13
  true
12
14
  end
13
15
 
14
- desc "db SUBCOMMAND", "Database operations"
15
- subcommand "db", DB::CLI
16
+ desc 'db SUBCOMMAND', 'Database operations'
17
+ subcommand 'db', DB::CLI
16
18
 
17
- desc "import SUBCOMMAND", "Import activities from local files"
18
- subcommand "import", ImportCLI
19
+ desc 'import SUBCOMMAND', 'Import activities from local files'
20
+ subcommand 'import', ImportCLI
19
21
 
20
- desc "export SUBCOMMAND", "Export activities to various formats"
21
- subcommand "export", ExportCLI
22
+ desc 'export SUBCOMMAND', 'Export activities to various formats'
23
+ subcommand 'export', ExportCLI
22
24
 
23
- desc "version", "Show version"
25
+ desc 'version', 'Show version'
24
26
  def version
25
27
  puts "git-fit v#{GitFit::VERSION}"
26
28
  end
27
29
 
28
- desc "parse FILE", "Parse a GPX or TCX file"
29
- option :save, type: :boolean, desc: "Save parsed activities to database", aliases: "-s"
30
+ desc 'parse FILE', 'Parse a GPX or TCX file'
31
+ option :save, type: :boolean, desc: 'Save parsed activities to database', aliases: '-s'
30
32
  def parse(file)
31
33
  format = file.match?(/\.gpx$/i) ? :gpx : :tcx
32
34
  content = File.read(file)
33
- parser = format == :gpx ? GitFit::Parser::GPX.new(source: "file") : GitFit::Parser::TCX.new(source: "file")
35
+ parser = format == :gpx ? GitFit::Parser::GPX.new(source: 'file') : GitFit::Parser::TCX.new(source: 'file')
34
36
  activities = parser.call(content)
35
37
 
36
38
  if activities.empty?
@@ -39,7 +41,7 @@ module GitFit
39
41
  end
40
42
 
41
43
  activities.each do |act|
42
- say_status :activity, act[:name] || "unnamed", :green
44
+ say_status :activity, act[:name] || 'unnamed', :green
43
45
  say " #{act[:sport_category]}#{act[:sport_type] ? " (#{act[:sport_type]})" : ""}"
44
46
  say " #{act[:start_date]} | #{act[:distance]&.round(2)}m" if act[:distance]
45
47
  say " #{act[:moving_time]}s" if act[:moving_time]
@@ -58,30 +60,30 @@ module GitFit
58
60
  end
59
61
  say_status :done, "Saved #{activities.size} activities", :green
60
62
  end
61
- rescue => e
63
+ rescue StandardError => e
62
64
  say_status :error, "Parse failed: #{e.message}", :red
63
65
  end
64
66
 
65
- desc "sync", "Sync workout data from a source"
66
- option :source, type: :string, desc: "Source name (e.g. lorem)"
67
- option :activity, type: :array, desc: "Filter by sport type", aliases: "--act"
67
+ desc 'sync', 'Sync workout data from a source'
68
+ option :source, type: :string, desc: 'Source name (e.g. lorem)'
69
+ option :activity, type: :array, desc: 'Filter by sport type', aliases: '--act'
68
70
  def sync
69
71
  super
70
72
  end
71
73
 
72
- desc "gh SUBCOMMAND", "Manage GitHub secrets and variables"
73
- subcommand "gh", GhCLI
74
+ desc 'gh SUBCOMMAND', 'Manage GitHub secrets and variables'
75
+ subcommand 'gh', GhCLI
74
76
 
75
- desc "geo SUBCOMMAND", "Detect geographical administrative regions"
76
- subcommand "geo", GeoCLI
77
+ desc 'geo SUBCOMMAND', 'Detect geographical administrative regions'
78
+ subcommand 'geo', GeoCLI
77
79
 
78
- desc "install SUBCOMMAND", "Install project assets (actions)"
79
- subcommand "install", InstallCLI
80
+ desc 'install SUBCOMMAND', 'Install project assets (actions)'
81
+ subcommand 'install', InstallCLI
80
82
 
81
- desc "init", "Generate config.yml"
82
- option :output, type: :string, desc: "Output path", aliases: "-o"
83
+ desc 'init', 'Generate config.yml'
84
+ option :output, type: :string, desc: 'Output path', aliases: '-o'
83
85
  def init
84
- path = options[:output] || "config/config.yml"
86
+ path = options[:output] || 'config/config.yml'
85
87
  FileUtils.mkdir_p(File.dirname(path))
86
88
 
87
89
  if File.exist?(path)
@@ -1,32 +1,34 @@
1
- require "yaml"
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
2
4
 
3
5
  module GitFit
4
6
  # 三优先级: env var > YAML > DEFAULTS
5
7
  class Config
6
8
  DEFAULTS = {
7
- "database" => { "path" => "db/fit.sqlite3" },
8
- "sync" => {
9
- "workers" => 3,
10
- "time_budget" => nil,
11
- "total_timeout" => nil
9
+ 'database' => { 'path' => 'db/fit.sqlite3' },
10
+ 'sync' => {
11
+ 'workers' => 3,
12
+ 'time_budget' => nil,
13
+ 'total_timeout' => nil,
14
+ },
15
+ 'export' => {
16
+ 'json' => { 'path' => 'site/activities.json' },
17
+ 'svg' => { 'dir' => 'site/svg' },
18
+ 'csv' => { 'path' => 'site/workouts.csv' },
19
+ 'gpx' => { 'path' => 'site/gpx_out' },
12
20
  },
13
- "export" => {
14
- "json" => { "path" => "site/activities.json" },
15
- "svg" => { "dir" => "site/svg" },
16
- "csv" => { "path" => "site/workouts.csv" },
17
- "gpx" => { "path" => "site/gpx_out" }
21
+ 'privacy' => {
22
+ 'start_end_range' => 200,
18
23
  },
19
- "privacy" => {
20
- "start_end_range" => 200
24
+ 'system' => {
25
+ 'units' => 'metric',
26
+ 'log_level' => 'info',
27
+ 'timezone' => 'Asia/Shanghai',
21
28
  },
22
- "system" => {
23
- "units" => "metric",
24
- "log_level" => "info",
25
- "timezone" => "Asia/Shanghai"
26
- }
27
29
  }.freeze
28
30
 
29
- DEFAULT_PATH = "config/config.yml"
31
+ DEFAULT_PATH = 'config/config.yml'
30
32
  KNOWN_TOP_KEYS = %w[database sync export privacy system].freeze
31
33
 
32
34
  def initialize(path = nil)
@@ -46,29 +48,41 @@ module GitFit
46
48
  end
47
49
 
48
50
  def sync_config(source)
49
- val = @data.dig("sync", source)
51
+ val = @data.dig('sync', source)
50
52
  val.is_a?(Hash) ? val : {}
51
53
  end
52
54
 
53
55
  def privacy_config
54
- @data["privacy"] || {}
56
+ @data['privacy'] || {}
55
57
  end
56
58
 
57
59
  def export_config
58
- @data["export"] || {}
60
+ @data['export'] || {}
59
61
  end
60
62
 
61
63
  def db_path
62
- @data.dig("database", "path") || DEFAULTS["database"]["path"]
64
+ @data.dig('database', 'path') || DEFAULTS['database']['path']
63
65
  end
64
66
 
65
67
  private
66
68
 
67
69
  def load_env
68
- @data["database"]["path"] = ENV["GIT_FIT_DATABASE_PATH"] if ENV["GIT_FIT_DATABASE_PATH"] && !ENV["GIT_FIT_DATABASE_PATH"].empty?
69
- @data["sync"]["workers"] = ENV["GIT_FIT_SYNC_WORKERS"].to_i if ENV["GIT_FIT_SYNC_WORKERS"] && !ENV["GIT_FIT_SYNC_WORKERS"].empty?
70
- @data["sync"]["time_budget"] = ENV["GIT_FIT_SYNC_TIME_BUDGET"].to_f if ENV["GIT_FIT_SYNC_TIME_BUDGET"] && !ENV["GIT_FIT_SYNC_TIME_BUDGET"].empty?
71
- @data["sync"]["total_timeout"] = ENV["GIT_FIT_SYNC_TOTAL_TIMEOUT"].to_f if ENV["GIT_FIT_SYNC_TOTAL_TIMEOUT"] && !ENV["GIT_FIT_SYNC_TOTAL_TIMEOUT"].empty?
70
+ if ENV['GIT_FIT_DATABASE_PATH'] && !ENV['GIT_FIT_DATABASE_PATH'].empty?
71
+ @data['database']['path'] =
72
+ ENV['GIT_FIT_DATABASE_PATH']
73
+ end
74
+ if ENV['GIT_FIT_SYNC_WORKERS'] && !ENV['GIT_FIT_SYNC_WORKERS'].empty?
75
+ @data['sync']['workers'] =
76
+ ENV['GIT_FIT_SYNC_WORKERS'].to_i
77
+ end
78
+ if ENV['GIT_FIT_SYNC_TIME_BUDGET'] && !ENV['GIT_FIT_SYNC_TIME_BUDGET'].empty?
79
+ @data['sync']['time_budget'] =
80
+ ENV['GIT_FIT_SYNC_TIME_BUDGET'].to_f
81
+ end
82
+ if ENV['GIT_FIT_SYNC_TOTAL_TIMEOUT'] && !ENV['GIT_FIT_SYNC_TOTAL_TIMEOUT'].empty?
83
+ @data['sync']['total_timeout'] =
84
+ ENV['GIT_FIT_SYNC_TOTAL_TIMEOUT'].to_f
85
+ end
72
86
 
73
87
  GitFit.registered_configs.each do |reg|
74
88
  reg[:keys].each do |key|
@@ -89,7 +103,7 @@ module GitFit
89
103
  def load_file(path)
90
104
  yaml = YAML.safe_load(File.read(path)) || {}
91
105
  deep_merge!(@data, yaml)
92
- rescue => e
106
+ rescue StandardError => e
93
107
  warn "Config: failed to load #{path}: #{e.message}"
94
108
  end
95
109
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitFit
2
4
  class ConfigTemplate
3
5
  def self.generate
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Deprecated: use GitFit.activity_to_hash / GitFit.format_time instead.
2
4
  # Kept for backward compatibility during migration.
3
5
  module GitFit
@@ -1,27 +1,29 @@
1
- require "thor"
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
2
4
 
3
5
  module GitFit
4
6
  module DB
5
7
  class CLI < Thor
6
- desc "migrate", "Run database migrations"
8
+ desc 'migrate', 'Run database migrations'
7
9
  def migrate
8
10
  config = GitFit::Config.new
9
11
  conn = GitFit::DB::Connection.new(config.db_path)
10
12
  conn.migrate!
11
- say_status :done, "Migrations up to date", :green
12
- rescue => e
13
+ say_status :done, 'Migrations up to date', :green
14
+ rescue StandardError => e
13
15
  say_status :error, "Migration failed: #{e.message}", :red
14
16
  end
15
17
 
16
- desc "schema", "Print DB schema DDL derived from migrations"
17
- option :hash, type: :boolean, desc: "Print sha256 hexdigest instead of DDL"
18
+ desc 'schema', 'Print DB schema DDL derived from migrations'
19
+ option :hash, type: :boolean, desc: 'Print sha256 hexdigest instead of DDL'
18
20
  def schema
19
- require "digest"
21
+ require 'digest'
20
22
  db = Sequel.sqlite
21
23
  Sequel.extension :migration
22
- dir = File.expand_path("../../../db/migrations", __dir__)
24
+ dir = File.expand_path('../../../db/migrations', __dir__)
23
25
  Sequel::Migrator.run(db, dir)
24
- ddl = db.fetch("SELECT sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY name")
26
+ ddl = db.fetch('SELECT sql FROM sqlite_master WHERE sql IS NOT NULL ORDER BY name')
25
27
  .map(:sql).join("\n") + "\n"
26
28
  if options[:hash]
27
29
  puts Digest::SHA256.hexdigest(ddl)
@@ -1,15 +1,17 @@
1
- require "fileutils"
2
- require "sequel"
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'sequel'
3
5
 
4
6
  module GitFit
5
7
  module DB
6
8
  class Connection
7
9
  def initialize(db_path)
8
- FileUtils.mkdir_p(File.dirname(db_path)) if db_path.include?("/")
10
+ FileUtils.mkdir_p(File.dirname(db_path)) if db_path.include?('/')
9
11
  @db = Sequel.sqlite(db_path)
10
- @db.run("PRAGMA journal_mode=WAL")
11
- @db.run("PRAGMA busy_timeout=5000")
12
- @migrations_path = File.expand_path("../../../db/migrations", __dir__)
12
+ @db.run('PRAGMA journal_mode=WAL')
13
+ @db.run('PRAGMA busy_timeout=5000')
14
+ @migrations_path = File.expand_path('../../../db/migrations', __dir__)
13
15
  end
14
16
 
15
17
  def db
@@ -1,4 +1,6 @@
1
- require "fileutils"
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
2
4
 
3
5
  module GitFit
4
6
  module DB
@@ -18,7 +20,7 @@ module GitFit
18
20
  data = {}
19
21
  (tables - migration_tables).each { |t| data[t] = export_table(db, t) }
20
22
 
21
- db.run("PRAGMA foreign_keys = OFF")
23
+ db.run('PRAGMA foreign_keys = OFF')
22
24
  tables.each { |t| db.drop_table(t) }
23
25
 
24
26
  Sequel.extension :migration
@@ -31,7 +33,7 @@ module GitFit
31
33
  end
32
34
 
33
35
  warn "Rebuilt #{db_path}: #{data.values.sum(&:size)} rows from #{data.size} tables"
34
- rescue => e
36
+ rescue StandardError => e
35
37
  if backup && File.exist?(backup)
36
38
  FileUtils.cp(backup, db_path)
37
39
  %w[-wal -shm].each do |ext|
@@ -1,28 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitFit
2
4
  module Export
3
5
  module Defaults
4
6
  SVG = {
5
- background: "#222222", text: "#FFFFFF", track: "#4DD2FF",
6
- track2: "#4DD2FF", special: "#FFFF00", special2: "#FFFF00",
7
- empty_cell: "#444444", dim_past: "#555555", font: "Arial",
7
+ background: '#222222', text: '#FFFFFF', track: '#4DD2FF',
8
+ track2: '#4DD2FF', special: '#FFFF00', special2: '#FFFF00',
9
+ empty_cell: '#444444', dim_past: '#555555', font: 'Arial'
8
10
  }.freeze
9
11
 
10
12
  DASHBOARD = {
11
- background: "#1a1a2e", surface: "#16213e", hover: "#0f3460",
12
- active_row: "#1a3a6a", border: "#2a2a4a", text: "#e0e0e0",
13
- text_secondary: "#888", text_dim: "#666", text_insight: "#aaa",
14
- accent: "#4DD2FF", font: "-apple-system, BlinkMacSystemFont, sans-serif",
13
+ background: '#1a1a2e', surface: '#16213e', hover: '#0f3460',
14
+ active_row: '#1a3a6a', border: '#2a2a4a', text: '#e0e0e0',
15
+ text_secondary: '#888', text_dim: '#666', text_insight: '#aaa',
16
+ accent: '#4DD2FF', font: '-apple-system, BlinkMacSystemFont, sans-serif'
15
17
  }.freeze
16
18
 
17
19
  CATEGORIES = {
18
- "run" => "#4DD2FF", "walk" => "#4ECDC4",
19
- "ride" => "#FF6B6B", "hike" => "#FFD93D",
20
+ 'run' => '#4DD2FF', 'walk' => '#4ECDC4',
21
+ 'ride' => '#FF6B6B', 'hike' => '#FFD93D'
20
22
  }.freeze
21
23
 
22
24
  MAP = {
23
- tiles_dark: "https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png",
24
- tiles_light: "https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png",
25
- echarts_theme: "dark",
25
+ tiles_dark: 'https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png',
26
+ tiles_light: 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png',
27
+ echarts_theme: 'dark',
26
28
  }.freeze
27
29
  end
28
30
  end
@@ -1,9 +1,11 @@
1
- require "json"
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
2
4
 
3
5
  module GitFit
4
6
  module Export
5
7
  class JSON
6
- PLACEHOLDER_POLYLINE = "gqqrFurkeU??".freeze
8
+ PLACEHOLDER_POLYLINE = 'gqqrFurkeU??'
7
9
 
8
10
  def initialize(db:, output:, activity_filter: nil)
9
11
  @db = db
@@ -49,7 +51,7 @@ module GitFit
49
51
  start_date: a[:start_date],
50
52
  start_date_local: a[:start_date_local],
51
53
  location_country: a[:location_country],
52
- summary_polyline: a[:source] == "keep" && a[:summary_polyline] == PLACEHOLDER_POLYLINE ?
54
+ summary_polyline: a[:source] == 'keep' && a[:summary_polyline] == PLACEHOLDER_POLYLINE ?
53
55
  nil : a[:summary_polyline],
54
56
  average_heartrate: a[:average_heartrate] && a[:average_heartrate] != 0 ? a[:average_heartrate] : nil,
55
57
  max_heartrate: a[:max_heartrate],
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitFit
2
4
  module Export
3
5
  end
4
6
  end
5
7
 
6
- require_relative "export/defaults"
7
- require_relative "export/json"
8
+ require_relative 'export/defaults'
9
+ require_relative 'export/json'
@@ -1,25 +1,27 @@
1
- require "mini_racer"
2
- require "json"
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_racer'
4
+ require 'json'
3
5
 
4
6
  module GitFit
5
7
  module FIT
6
8
  class Decoder
7
- JS_CODE = File.read(File.join(__dir__, "decoder.js")).freeze
9
+ JS_CODE = File.read(File.join(__dir__, 'decoder.js')).freeze
8
10
  CTX = MiniRacer::Context.new.tap { |c| c.eval(JS_CODE) }
9
11
 
10
12
  def self.decode(path)
11
- bytes = File.read(path, mode: "rb").bytes
13
+ bytes = File.read(path, mode: 'rb').bytes
12
14
  json = CTX.eval("FitDecoder.decodeFit(#{bytes.inspect})")
13
15
  records = JSON.parse(json)
14
16
  records.each do |r|
15
- r["positionLat"] *= 180 if r["positionLat"]
16
- r["positionLong"] *= 180 if r["positionLong"]
17
+ r['positionLat'] *= 180 if r['positionLat']
18
+ r['positionLong'] *= 180 if r['positionLong']
17
19
  end
18
20
  records
19
21
  end
20
22
 
21
23
  def self.sport(path)
22
- bytes = File.read(path, mode: "rb").bytes
24
+ bytes = File.read(path, mode: 'rb').bytes
23
25
  CTX.eval("FitDecoder.fitSport(#{bytes.inspect})")
24
26
  end
25
27
  end
data/lib/git_fit/fit.rb CHANGED
@@ -1 +1,3 @@
1
- require_relative "fit/decoder"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'fit/decoder'
@@ -1,11 +1,13 @@
1
- require "net/http"
2
- require "json"
3
- require "uri"
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
4
6
 
5
7
  module GitFit
6
8
  module Geo
7
9
  class AMapClient
8
- BASE_URL = "https://restapi.amap.com/v3/geocode/regeo".freeze
10
+ BASE_URL = 'https://restapi.amap.com/v3/geocode/regeo'
9
11
 
10
12
  def initialize(api_key:)
11
13
  @api_key = api_key
@@ -20,13 +22,13 @@ module GitFit
20
22
  uri.query = URI.encode_www_form(
21
23
  key: @api_key,
22
24
  location: "#{lng},#{lat}",
23
- output: "JSON",
25
+ output: 'JSON',
24
26
  radius: 1000,
25
- extensions: "all",
27
+ extensions: 'all',
26
28
  )
27
29
  resp = Net::HTTP.get_response(uri)
28
30
  body = JSON.parse(resp.body)
29
- if body["status"] != "1"
31
+ if body['status'] != '1'
30
32
  raise "AMap API error: #{body["info"]}"
31
33
  end
32
34
  parse_response(body)
@@ -35,12 +37,12 @@ module GitFit
35
37
  private
36
38
 
37
39
  def parse_response(body)
38
- comp = body.dig("regeocode", "addressComponent") || {}
39
- province = comp["province"].to_s.empty? ? nil : comp["province"]
40
- city = comp["city"].to_s.empty? || comp["city"] == comp["province"] ? nil : comp["city"]
41
- district = comp["district"].to_s.empty? ? nil : comp["district"]
42
- township = comp["township"].to_s.empty? ? nil : comp["township"]
43
- { province: province, city: city, district: district, township: township, source: "amap" }
40
+ comp = body.dig('regeocode', 'addressComponent') || {}
41
+ province = comp['province'].to_s.empty? ? nil : comp['province']
42
+ city = comp['city'].to_s.empty? || comp['city'] == comp['province'] ? nil : comp['city']
43
+ district = comp['district'].to_s.empty? ? nil : comp['district']
44
+ township = comp['township'].to_s.empty? ? nil : comp['township']
45
+ { province: province, city: city, district: district, township: township, source: 'amap' }
44
46
  end
45
47
 
46
48
  def rate_limit
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitFit
2
4
  module Geo
3
5
  # 3 coordinate systems: WGS84 (GPS native), GCJ-02 (China offset), BD-09 (Baidu).