gtfs_stops_clustering 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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