gtfs_stops_clustering 0.1.1 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a9a16f2fa8980f4bd7e84e9912e7925caa556fd2b2daab56209ff8351e93a19
4
- data.tar.gz: 652b0895096c0b55009d669ea1b8416368dec5884b4a395ca4fd46c762836575
3
+ metadata.gz: d10d21f2d65afcf335560f35f8a34e4c8e46b3e6eeb6b7c74d9edf73e3336227
4
+ data.tar.gz: 67f4f260b43a6ac2f8af21b8c1a5471ee241b3c86f6a8532c55e0554268cdaa0
5
5
  SHA512:
6
- metadata.gz: 3ea8cd7f921ff06aa5e838684d1efb8fcaa48fa3b2601e85ed6f926fecebc9d4bb8831399d5bd0e63cf1a2531231e5d8c71b4e56a3287c9ef0aa602b22726a7f
7
- data.tar.gz: 87bea9a4711300f07b9b6a0151590de265e6c55447a9c1e6ad3b7503b1ffaa45ac5090e1b1859a4786765b1709826519f84876504faac815d7c85282ee1857ed
6
+ metadata.gz: e5ef45bb1402420422984e19918edfe60c5cc73fac8d503be3f605bd83c4b97460a688e79bba16e901cb42ecf358571cc1380467920e2c9b051be08b0fcff5fb
7
+ data.tar.gz: '0349797d0744abe6432ab20f262ac766118b2a3153b1e5dbb2796d2c3eb359fccc109bdadc17affbe43b9824457701daa5d3a1c434fe43ab40c3c1534dbce382'
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # lib/data_import.rb
2
4
 
3
- require 'csv'
4
- require 'gtfs'
5
+ require "csv"
6
+ require "gtfs"
5
7
 
8
+ # DataImport module
6
9
  module DataImport
7
- VERSION='0.0.1'
8
10
  attr_accessor :data_import
9
11
 
12
+ # DataImport class
10
13
  class DataImport
11
14
  attr_accessor :stops, :stops_config_file, :stops_names, :stops_corner_cases, :stops_data, :stops_redis_geodata
12
15
 
@@ -22,13 +25,13 @@ module DataImport
22
25
  end
23
26
 
24
27
  def import_stops_corner_cases
25
- if File.exist?(@stops_config_file)
26
- CSV.foreach(@stops_config_file, headers: true) do |row|
27
- stop_name = row['stop_name']
28
- cluster_name = row['cluster_name']
28
+ return unless File.exist?(@stops_config_file)
29
+
30
+ CSV.foreach(@stops_config_file, headers: true) do |row|
31
+ stop_name = row["stop_name"]
32
+ cluster_name = row["cluster_name"]
29
33
 
30
- stops_corner_cases << { stop_name: stop_name, cluster_name: cluster_name }
31
- end
34
+ stops_corner_cases << { stop_name: stop_name, cluster_name: cluster_name }
32
35
  end
33
36
  end
34
37
 
@@ -38,13 +41,20 @@ module DataImport
38
41
  longitude = row.lon
39
42
  stop_name = row.name
40
43
 
41
- stop_name = @stops_corner_cases.find { |entry| entry[:stop_name] == stop_name }[:cluster_name] if stops_corner_cases.find { |entry| entry[:stop_name] == stop_name }
44
+ stop_name = stop_name_from_corner_cases(stop_name)
42
45
 
43
46
  @stops_names << stop_name
44
47
  @stops_data << [latitude, longitude]
45
48
  @stops_redis_geodata << [longitude, latitude, "#{longitude},#{latitude}"]
46
49
  end
47
50
  end
51
+
52
+ def stop_name_from_corner_cases(stop_name)
53
+ csv_entry = @stops_corner_cases.find do |entry|
54
+ entry[:stop_name] == stop_name
55
+ end
56
+ csv_entry.nil? ? stop_name : csv_entry[:cluster_name]
57
+ end
48
58
  end
49
59
 
50
60
  def import_stops_data(*args)
@@ -56,5 +66,3 @@ module DataImport
56
66
  }
57
67
  end
58
68
  end
59
-
60
- include DataImport
@@ -1,33 +1,47 @@
1
- ## https://github.com/shiguodong/dbscan (fork)
1
+ # frozen_string_literal: true
2
2
 
3
- require 'distance_measures'
4
- require 'text'
5
- require 'geocoder'
6
- require_relative 'redis_geodata'
3
+ # lib/gtfs_stops_clustering/dbscan.rb
7
4
 
5
+ require "distance_measures"
6
+ require "text"
7
+ require "geocoder"
8
+ require_relative "redis_geodata"
9
+
10
+ # Array class
8
11
  class Array
9
- def haversine_distance2(n)
10
- Geocoder::Calculations.distance_between(self, n)
12
+ def haversine_distance2(other)
13
+ Geocoder::Calculations.distance_between(self, other)
11
14
  end
12
15
  end
13
16
 
17
+ # DBSCAN module
14
18
  module DBSCAN
19
+ # Clusterer class
15
20
  class Clusterer
21
+ include RedisGeodata
16
22
  attr_accessor :points, :options, :clusters
17
23
 
18
24
  def initialize(points, stops_redis_geodata, options = {})
19
25
  options[:distance] = :euclidean_distance unless options[:distance]
20
26
  options[:labels] = [] unless options[:labels]
21
27
 
22
- c = 0
23
28
  redis_geodata_import(stops_redis_geodata, options[:epsilon])
24
- @points = points.map { |e| po = Point.new(e, options[:labels][c]); c +=1; po }
25
29
  @options = options
26
- @clusters = {-1 => []}
30
+ init_points(points)
31
+ @clusters = { -1 => [] }
27
32
 
28
33
  clusterize!
29
34
  end
30
35
 
36
+ def init_points(points)
37
+ c = 0
38
+ @points = points.map do |e|
39
+ po = Point.new(e, @options[:labels][c])
40
+ c += 1
41
+ po
42
+ end
43
+ end
44
+
31
45
  def clusterize!
32
46
  current_cluster = -1
33
47
  @points.each do |point|
@@ -49,10 +63,10 @@ module DBSCAN
49
63
  # Get Cluster Position
50
64
  cluster_pos = find_cluster_position(clusters[current_cluster])
51
65
 
52
- clusters[current_cluster].each { |e|
66
+ clusters[current_cluster].each do |e|
53
67
  e.cluster_name = cluster_name
54
68
  e.cluster_pos = cluster_pos
55
- }
69
+ end
56
70
  else
57
71
  clusters[-1].push(point)
58
72
  end
@@ -91,9 +105,11 @@ module DBSCAN
91
105
  neighbors = []
92
106
  geosearch_results = geosearch(point.items[1], point.items[0])
93
107
  geosearch_results.each do |neighbor_pos|
94
- coordinates = neighbor_pos.split(',')
95
- neighbor = @points.find { |point| point.items[0] == coordinates[1] &&
96
- point.items[1] == coordinates[0] }
108
+ coordinates = neighbor_pos.split(",")
109
+ neighbor = @points.find do |elem|
110
+ elem.items[0] == coordinates[1] &&
111
+ elem.items[1] == coordinates[0]
112
+ end
97
113
  next unless neighbor
98
114
 
99
115
  string_distance = Text::Levenshtein.distance(point.label.downcase, neighbor.label.downcase)
@@ -112,9 +128,7 @@ module DBSCAN
112
128
 
113
129
  if new_points.size >= options[:min_points]
114
130
  new_points.each do |p|
115
- unless neighbors.include?(p)
116
- neighbors.push(p)
117
- end
131
+ neighbors.push(p) unless neighbors.include?(p)
118
132
  end
119
133
  end
120
134
  end
@@ -127,32 +141,31 @@ module DBSCAN
127
141
 
128
142
  cluster_points
129
143
  end
130
- end
131
144
 
132
- def find_cluster_name(labels)
133
- words = labels.map { |label| label.strip.split }
134
- common_title = ''
145
+ def find_cluster_name(labels)
146
+ words = labels.map { |label| label.strip.split }
147
+ common_title = ""
135
148
 
136
- # Loop through each word index starting from the first
137
- (0...words.first.length).each do |i|
138
- words_at_index = words.map { |word_list| word_list[i] }
149
+ # Loop through each word index starting from the first
150
+ (0...words.first.length).each do |i|
151
+ words_at_index = words.map { |word_list| word_list[i] }
139
152
 
140
- break unless words_at_index.uniq.length == 1
153
+ break unless words_at_index.uniq.length == 1
141
154
 
142
- common_title += " #{words_at_index.first.capitalize}"
143
- end
144
-
145
- common_title.strip! ? common_title : labels.first
146
- end
155
+ common_title += " #{words_at_index.first.capitalize}"
156
+ end
147
157
 
148
- def find_cluster_position(cluster)
149
- total_lat = cluster.map { |e| e.items[0].to_f }.sum
150
- total_lon = cluster.map { |e| e.items[1].to_f }.sum
151
- avg_lat = total_lat / cluster.size
152
- avg_lon = total_lon / cluster.size
153
- [avg_lat, avg_lon]
158
+ common_title.strip ? common_title : labels.first
159
+ end
160
+ def find_cluster_position(cluster)
161
+ total_lat = cluster.map { |e| e.items[0].to_f }.sum
162
+ total_lon = cluster.map { |e| e.items[1].to_f }.sum
163
+ avg_lat = total_lat / cluster.size
164
+ avg_lon = total_lon / cluster.size
165
+ [avg_lat, avg_lon]
166
+ end
154
167
  end
155
-
168
+ # Point class
156
169
  class Point
157
170
  attr_accessor :items, :cluster, :visited, :label, :cluster_name, :cluster_pos
158
171
 
@@ -176,5 +189,3 @@ module DBSCAN
176
189
  clusterer.labeled_results
177
190
  end
178
191
  end
179
-
180
- include DBSCAN
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/input_consistency_checks.rb
4
+
5
+ # InputConsistencyChecks module
6
+ module InputConsistencyChecks
7
+ # InputConsistencyChecks class
8
+ class InputConsistencyChecks
9
+ attr_accessor :gtfs_paths, :epsilon, :min_points, :names_similarity, :stops_config_path
10
+
11
+ def initialize(gtfs_paths, epsilon, min_points, names_similarity, stops_config_path)
12
+ @gtfs_paths = gtfs_paths
13
+ @stops_config_path = stops_config_path
14
+ @epsilon = epsilon
15
+ @min_points = min_points
16
+ @names_similarity = names_similarity
17
+ input_consistency_checks
18
+ end
19
+
20
+ def input_consistency_checks
21
+ gtfs_paths_check
22
+ epsilon_check
23
+ min_points_check
24
+ names_similarity_check
25
+ end
26
+
27
+ def gtfs_paths_check
28
+ raise ArgumentError, "gtfs_paths cannot be nil" if @gtfs_paths.nil?
29
+ raise ArgumentError, "gtfs_paths must be an Array" unless @gtfs_paths.is_a?(Array)
30
+ raise ArgumentError, "gtfs_paths must not be empty" if @gtfs_paths.empty?
31
+ end
32
+
33
+ def epsilon_check
34
+ raise ArgumentError, "epsilon must be a Float" unless @epsilon.is_a?(Float)
35
+ raise ArgumentError, "epsilon must be greater than 0" if @epsilon.negative?
36
+ end
37
+
38
+ def min_points_check
39
+ raise ArgumentError, "min_points must be an Integer" unless @min_points.is_a?(Integer)
40
+ raise ArgumentError, "min_points must be greater than 0" if @min_points.negative?
41
+ end
42
+
43
+ def names_similarity_check
44
+ raise ArgumentError, "names_similarity must be a Float" unless @names_similarity.is_a?(Float)
45
+ raise ArgumentError, "names_similarity must be between 0 and 1" if @names_similarity.negative? || @names_similarity > 1
46
+ end
47
+ end
48
+
49
+ def input_consistency_checks(gtfs_paths, epsilon, min_points, names_similarity, stop_config_path)
50
+ @input_consistency_checks = InputConsistencyChecks.new(gtfs_paths, epsilon, min_points, names_similarity, stop_config_path)
51
+ end
52
+ end
@@ -1,17 +1,21 @@
1
- # lib/redis_geodata.rb
2
- require 'redis'
1
+ # frozen_string_literal: true
3
2
 
3
+ # lib/gtfs_stops_clustering/redis_geodata.rb
4
+
5
+ require "redis"
6
+
7
+ # RedisGeodata module
4
8
  module RedisGeodata
5
- VERSION='0.0.1'
6
9
  attr_accessor :redis
7
10
 
11
+ # RedisGeodata class
8
12
  class RedisGeodata
9
13
  attr_accessor :stops, :key, :redis, :epsilon
10
14
 
11
15
  def initialize(stops, epsilon)
12
- @redis = Redis.new(url: 'redis://127.0.0.1:6379')
16
+ @redis = Redis.new(url: "redis://127.0.0.1:6379")
13
17
  @stops = stops
14
- @key = 'stops'
18
+ @key = "stops"
15
19
  @epsilon = epsilon
16
20
  geoadd
17
21
  end
@@ -22,8 +26,8 @@ module RedisGeodata
22
26
  end
23
27
 
24
28
  def geosearch(longitude, latitude)
25
- list = @redis.georadius(@key, longitude, latitude, @epsilon, 'km')
26
- list.reject! { |point| point == longitude.to_s + "," + latitude.to_s }
29
+ list = @redis.georadius(@key, longitude, latitude, @epsilon, "km")
30
+ list.reject! { |point| point == "#{longitude},#{latitude}" }
27
31
  list
28
32
  end
29
33
  end
@@ -36,5 +40,3 @@ module RedisGeodata
36
40
  @redis.geosearch(*args)
37
41
  end
38
42
  end
39
-
40
- include RedisGeodata
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GtfsStopsClustering
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.3"
5
5
  end
@@ -1,28 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # lib/gtfs_stops_clustering.rb
2
4
 
3
- require 'gtfs'
4
- require 'csv'
5
- require_relative './gtfs_stops_clustering/data_import'
6
- require_relative './gtfs_stops_clustering/dbscan'
5
+ require "rubygems"
6
+ require "bundler/setup"
7
+ require_relative "gtfs_stops_clustering/version"
8
+ require "gtfs"
9
+ require "csv"
10
+ require_relative "./gtfs_stops_clustering/data_import"
11
+ require_relative "./gtfs_stops_clustering/dbscan"
12
+ require_relative "./gtfs_stops_clustering/input_consistency_checks"
7
13
 
14
+ # GtfsStopClustering module
8
15
  module GtfsStopsClustering
9
- VERSION='0.0.1'
10
16
  attr_accessor :gtfs_stops_clustering
11
17
 
18
+ # GtfsStopsClustering class
12
19
  class GtfsStopsClustering
13
- attr_accessor :clusters, :gtfs_urls, :gtfs_stops, :stops_config_path, :epsilon, :min_points, :names_similarity
20
+ include InputConsistencyChecks
21
+ include DataImport
22
+ include DBSCAN
23
+ attr_accessor :clusters, :gtfs_paths, :gtfs_stops, :stops_config_path, :epsilon, :min_points, :names_similarity
14
24
 
15
- def initialize(gtfs_urls, epsilon, min_points, names_similarity, stops_config_path)
25
+ def initialize(gtfs_paths, epsilon, min_points, names_similarity, stops_config_path)
26
+ @gtfs_paths = gtfs_paths
27
+ @stops_config_path = stops_config_path
28
+ @epsilon = epsilon
29
+ @min_points = min_points
30
+ @names_similarity = names_similarity
31
+ input_consistency_checks(@gtfs_paths, @epsilon, @min_points, @names_similarity, @stops_config_path)
16
32
  @clusters = []
17
- unless gtfs_urls.empty?
18
- @gtfs_paths = gtfs_urls
19
- @stops_config_path = stops_config_path
20
- @epsilon = epsilon
21
- @min_points = min_points
22
- @names_similarity = names_similarity
23
- @gtfs_stops = create_stops_merged
24
- clusterize_stops_csv(@gtfs_stops)
25
- end
33
+ @gtfs_stops = create_stops_merged
34
+ clusterize_stops
26
35
  end
27
36
 
28
37
  def create_stops_merged
@@ -34,11 +43,20 @@ module GtfsStopsClustering
34
43
  gtfs_stops.flatten
35
44
  end
36
45
 
37
- def clusterize_stops_csv(stops_merged)
38
- data = import_stops_data(stops_merged, @stops_config_path)
39
- @clusters = DBSCAN( data[:stops_data], data[:stops_redis_geodata], :epsilon => @epsilon, :min_points => @min_points, :similarity => @names_similarity, :distance => :haversine_distance2, :labels => data[:stops_names] )
46
+ def clusterize_stops
47
+ data = import_stops_data(@gtfs_stops, @stops_config_path)
48
+ @clusters = DBSCAN(data[:stops_data],
49
+ data[:stops_redis_geodata],
50
+ epsilon: @epsilon,
51
+ min_points: @min_points,
52
+ similarity: @names_similarity,
53
+ distance: :haversine_distance2,
54
+ labels: data[:stops_names])
55
+ map_clustered_stops
56
+ end
40
57
 
41
- @clusters.each do |cluster_id, cluster|
58
+ def map_clustered_stops
59
+ @clusters.each_value do |cluster|
42
60
  cluster.each do |stop|
43
61
  gtfs_stop = @gtfs_stops.find { |e| e.lat == stop[:stop_lat] && e.lon == stop[:stop_lon] }
44
62
  stop[:stop_id] = gtfs_stop.id
@@ -46,24 +64,11 @@ module GtfsStopsClustering
46
64
  stop[:parent_station] = gtfs_stop.parent_station
47
65
  end
48
66
  end
49
-
50
- output_path = 'stop_clusters.txt'
51
- File.open(output_path, 'w') do |file|
52
- @clusters.each do |cluster_id, cluster |
53
- file.puts "Cluster #{cluster_id}"
54
- cluster.each do |point|
55
- file.puts point.inspect
56
- end
57
- file.puts
58
- end
59
- end
60
67
  end
61
68
  end
62
69
 
63
- def gtfs_stops_clusters(gtfs_urls, epsilon, min_points, names_similarity = 1, stop_config_path = '')
64
- @gtfs_stops_clustering = GtfsStopsClustering.new(gtfs_urls, epsilon, min_points, names_similarity, stop_config_path)
70
+ def build_clusters(gtfs_paths, epsilon, min_points, names_similarity = 1, stop_config_path = "")
71
+ @gtfs_stops_clustering = GtfsStopsClustering.new(gtfs_paths, epsilon, min_points, names_similarity, stop_config_path)
65
72
  @gtfs_stops_clustering.clusters
66
73
  end
67
74
  end
68
-
69
- include GtfsStopsClustering
metadata CHANGED
@@ -1,49 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gtfs_stops_clustering
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
- - Visco01
7
+ - Pietro Visconti
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-06 00:00:00.000000000 Z
11
+ date: 2023-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: distance_measures
14
+ name: csv
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.6
19
+ version: '3.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.2.8
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - "~>"
25
28
  - !ruby/object:Gem::Version
26
- version: 0.0.6
29
+ version: '3.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.2.8
27
33
  - !ruby/object:Gem::Dependency
28
- name: text
34
+ name: distance_measures
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '1.3'
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 1.3.1
39
+ version: 0.0.6
37
40
  type: :runtime
38
41
  prerelease: false
39
42
  version_requirements: !ruby/object:Gem::Requirement
40
43
  requirements:
41
44
  - - "~>"
42
45
  - !ruby/object:Gem::Version
43
- version: '1.3'
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 1.3.1
46
+ version: 0.0.6
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: geocoder
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -65,25 +65,19 @@ dependencies:
65
65
  - !ruby/object:Gem::Version
66
66
  version: 1.8.2
67
67
  - !ruby/object:Gem::Dependency
68
- name: csv
68
+ name: gtfs
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '3.2'
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: 3.2.8
73
+ version: 0.4.1
77
74
  type: :runtime
78
75
  prerelease: false
79
76
  version_requirements: !ruby/object:Gem::Requirement
80
77
  requirements:
81
78
  - - "~>"
82
79
  - !ruby/object:Gem::Version
83
- version: '3.2'
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: 3.2.8
80
+ version: 0.4.1
87
81
  - !ruby/object:Gem::Dependency
88
82
  name: redis
89
83
  requirement: !ruby/object:Gem::Requirement
@@ -105,23 +99,57 @@ dependencies:
105
99
  - !ruby/object:Gem::Version
106
100
  version: 5.0.8
107
101
  - !ruby/object:Gem::Dependency
108
- name: gtfs
102
+ name: text
109
103
  requirement: !ruby/object:Gem::Requirement
110
104
  requirements:
111
105
  - - "~>"
112
106
  - !ruby/object:Gem::Version
113
- version: 0.4.1
107
+ version: '1.3'
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
114
111
  type: :runtime
115
112
  prerelease: false
116
113
  version_requirements: !ruby/object:Gem::Requirement
117
114
  requirements:
118
115
  - - "~>"
119
116
  - !ruby/object:Gem::Version
120
- version: 0.4.1
117
+ version: '1.3'
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 1.3.1
121
+ - !ruby/object:Gem::Dependency
122
+ name: minitest
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '5.20'
128
+ type: :development
129
+ prerelease: false
130
+ version_requirements: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '5.20'
135
+ - !ruby/object:Gem::Dependency
136
+ name: pry
137
+ requirement: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: 0.14.2
142
+ type: :development
143
+ prerelease: false
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 0.14.2
121
149
  description: A gem to read GTFS stops data and create clusters based on coordinates
122
150
  and stop names' similarities.
123
151
  email:
124
- - visconti373@gmail.com
152
+ - pietro.visconti2001@gmail.com
125
153
  executables: []
126
154
  extensions: []
127
155
  extra_rdoc_files: []
@@ -129,12 +157,16 @@ files:
129
157
  - lib/gtfs_stops_clustering.rb
130
158
  - lib/gtfs_stops_clustering/data_import.rb
131
159
  - lib/gtfs_stops_clustering/dbscan.rb
160
+ - lib/gtfs_stops_clustering/input_consistency_checks.rb
132
161
  - lib/gtfs_stops_clustering/redis_geodata.rb
133
162
  - lib/gtfs_stops_clustering/version.rb
134
- homepage:
163
+ homepage: https://github.com/Visco01/gtfs_stops_clustering
135
164
  licenses:
136
165
  - MIT
137
- metadata: {}
166
+ metadata:
167
+ homepage_uri: https://github.com/Visco01/gtfs_stops_clustering
168
+ source_code_uri: https://github.com/Visco01/gtfs_stops_clustering
169
+ changelog_uri: https://github.com/Visco01/gtfs_stops_clustering/blob/main/CHANGELOG.md
138
170
  post_install_message:
139
171
  rdoc_options: []
140
172
  require_paths:
@@ -150,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
182
  - !ruby/object:Gem::Version
151
183
  version: '0'
152
184
  requirements: []
153
- rubygems_version: 3.4.10
185
+ rubygems_version: 3.4.22
154
186
  signing_key:
155
187
  specification_version: 4
156
188
  summary: A gem to read GTFS stops data and create clusters based on coordinates and