File Coverage

blib/lib/Map/Metro.pm
Criterion Covered Total %
statement 32 55 58.1
branch 0 8 0.0
condition n/a
subroutine 11 16 68.7
pod 2 4 50.0
total 45 83 54.2


line stmt bran cond sub pod time code
1 1     1   35560 use 5.16.0;
  1         3  
2 1     1   479 use Map::Metro::Standard;
  1         2  
  1         8  
3 1     1   12920 use strict;
  1         2  
  1         22  
4 1     1   4 use warnings;
  1         2  
  1         61  
5              
6             package Map::Metro {
7              
8             our $VERSION = '0.2300'; # VERSION
9             # ABSTRACT: Public transport graphing
10              
11 1     1   938 use Moose;
  1         463025  
  1         6  
12 1     1   7773 use Module::Pluggable search_path => ['Map::Metro::Plugin::Map'], require => 1, sub_name => 'system_maps';
  1         9185  
  1         9  
13 1     1   955 use MooseX::AttributeShortcuts;
  1         403393  
  1         6  
14 1     1   37380 use Types::Standard -types;
  1         65731  
  1         14  
15 1     1   5135 use Types::Path::Tiny -types;
  1         22796  
  1         12  
16 1     1   933 use List::Util 1.33 'any';
  1         23  
  1         60  
17              
18 1     1   880 use Map::Metro::Graph;
  1         4  
  1         1331  
19              
20             has map => (
21             is => 'ro',
22             traits => ['Array'],
23             isa => ArrayRef,
24             predicate => 1,
25             handles => {
26             get_map => 'get',
27             },
28             );
29             has mapclasses => (
30             is => 'ro',
31             traits => ['Array'],
32             isa => ArrayRef,
33             default => sub { [] },
34             handles => {
35             add_mapclass => 'push',
36             get_mapclass => 'get',
37             },
38             );
39             has hooks => (
40             is => 'ro',
41             isa => ArrayRef[ Str ],
42             traits => ['Array'],
43             default => sub { [] },
44             handles => {
45             all_hooks => 'elements',
46             hook_count => 'count',
47             },
48             );
49             has _plugin_ns => (
50             is => 'ro',
51             isa => Str,
52             default => 'Plugin::Map',
53             init_arg => undef,
54             );
55              
56             around BUILDARGS => sub {
57             my ($orig, $class, @args) = @_;
58             my %args;
59             if(scalar @args == 1) {
60             $args{'map'} = shift @args;
61             }
62             elsif(scalar @args % 2 != 0) {
63             my $map = shift @args;
64             %args = @args;
65             $args{'map'} = $map;
66             }
67             else {
68             %args = @args;
69             }
70              
71             if(exists $args{'map'} && !ArrayRef->check($args{'map'})) {
72             $args{'map'} = [$args{'map'}];
73             }
74             if(exists $args{'hooks'} && !ArrayRef->check($args{'hooks'})) {
75             $args{'hooks'} = [$args{'hooks'}];
76             }
77              
78             return $class->$orig(%args);
79             };
80              
81             sub BUILD {
82 0     0 0   my $self = shift;
83 0           my @args = @_;
84              
85 0 0         if($self->has_map) {
86 0           my @system_maps = map { s{^Map::Metro::Plugin::Map::}{}; $_ } $self->system_maps;
  0            
  0            
87 0 0   0     if(any { $_ eq $self->get_map(0) } @system_maps) {
  0            
88 0           my $mapclass = 'Map::Metro::Plugin::Map::'.$self->get_map(0);
89 0           my $mapobj = $mapclass->new(hooks => $self->hooks);
90 0           $self->add_mapclass($mapobj);
91             }
92             }
93             }
94              
95             # Borrowed from Mojo::Util
96             sub decamelize {
97 0     0 0   my $self = shift;
98 0           my $string = shift;
99              
100 0 0         return $string if $string !~ m{[A-Z]};
101             return join '_' => map {
102 0           join ('_' => map { lc } grep { length } split m{([A-Z]{1}[^A-Z]*)})
  0            
  0            
  0            
103             } split '::' => $string;
104             }
105              
106             sub parse {
107 0     0 1   my $self = shift;
108 0           my %args = @_;
109              
110             return Map::Metro::Graph->new(filepath => $self->get_mapclass(0)->maplocation,
111             do_undiacritic => $self->get_mapclass(0)->do_undiacritic,
112             wanted_hook_plugins => [$self->all_hooks],
113 0 0         exists $args{'override_line_change_weight'} ? (override_line_change_weight => $args{'override_line_change_weight'}) : (),
114             )->parse;
115             }
116              
117             sub available_maps {
118 0     0 1   my $self = shift;
119 0           return sort $self->system_maps;
120             }
121             }
122              
123             1;
124              
125             __END__
126              
127             =pod
128              
129             =encoding utf-8
130              
131             =head1 NAME
132              
133             Map::Metro - Public transport graphing
134              
135              
136              
137             =begin HTML
138              
139             <p><img src="https://img.shields.io/badge/perl-5.16+-brightgreen.svg" alt="Requires Perl 5.16+" /> <a href="https://travis-ci.org/Csson/p5-Map-Metro"><img src="https://api.travis-ci.org/Csson/p5-Map-Metro.svg?branch=master" alt="Travis status" /></a></p>
140              
141             =end HTML
142              
143              
144             =begin markdown
145              
146             ![Requires Perl 5.16+](https://img.shields.io/badge/perl-5.16+-brightgreen.svg) [![Travis status](https://api.travis-ci.org/Csson/p5-Map-Metro.svg?branch=master)](https://travis-ci.org/Csson/p5-Map-Metro)
147              
148             =end markdown
149              
150             =head1 VERSION
151              
152             Version 0.2300, released 2016-01-14.
153              
154             =head1 SYNOPSIS
155              
156             # Install a map
157             $ cpanm Map::Metro::Plugin::Map::Stockholm
158              
159             # And then
160             my $graph = Map::Metro->new('Stockholm', hooks => ['PrettyPrinter'])->parse;
161              
162             my $routing = $graph->routing_for('Universitetet', 'Kista');
163              
164             # or in a terminal
165             $ map-metro.pl route Stockholm Universitetet Kista
166              
167             prints
168              
169             From Universitetet to Kista
170             ===========================
171              
172             -- Route 1 (cost 15) ----------
173             [ T14 ] Universitetet
174             [ T14 ] Tekniska högskolan
175             [ T14 ] Stadion
176             [ T14 ] Östermalmstorg
177             [ T14 ] T-Centralen
178             [ * T11 ] T-Centralen
179             [ T11 ] RÃ¥dhuset
180             [ T11 ] Fridhemsplan
181             [ T11 ] Stadshagen
182             [ T11 ] Västra skogen
183             [ T11 ] Solna centrum
184             [ T11 ] Näckrosen
185             [ T11 ] Hallonbergen
186             [ T11 ] Kista
187              
188             T11 Blue line
189             T14 Red line
190              
191             *: Transfer to other line
192             +: Transfer to other station
193              
194             =head1 DESCRIPTION
195              
196             The purpose of this distribution is to find the shortest L<unique|/"What is a unique path?"> route/routes between two stations in a transport network.
197              
198             See L<Task::MapMetro::Maps> for a list of released maps.
199              
200             =head2 Methods
201              
202             =head3 new($city, hooks => [])
203              
204             B<C<$city>>
205              
206             The name of the city you want to search connections in. Mandatory, unless you are only going to call L</"available_maps">.
207              
208             B<C<$hooks>>
209              
210             Array reference of L<Hooks|Map::Metro::Hook> that listens for events.
211              
212             =head3 parse()
213              
214             Returns a L<Map::Metro::Graph> object containing the entire graph.
215              
216             =head3 available_maps()
217              
218             Returns an array reference containing the names of all Map::Metro maps installed on the system.
219              
220             =head2 What is a unique path?
221              
222             The following rules are a guideline:
223              
224             If the starting station and finishing station...
225              
226             ...is on the same line there will be no transfers to other lines.
227              
228             ...shares multiple lines (e.g., both stations are on both line 2 and 4), each line constitutes a route.
229              
230             ...are on different lines a transfer will take place at a shared station. No matter how many shared stations there are, there will only be one route returned (but which transfer station is used can differ between queries).
231              
232             ...has no shared stations, the shortest route/routes will be returned.
233              
234             =head1 MORE INFORMATION
235              
236             L<Map::Metro::Graph> - How to use graph object.
237              
238             L<Map::Metro::Plugin::Map> - How to make your own maps.
239              
240             L<Map::Metro::Hook> - How to extend Map::Metro via hooks/events.
241              
242             L<Map::Metro::Cmd> - A guide to the command line application.
243              
244             L<Map::Metro::Graph::Connection> - Defines a MMG::Connection.
245              
246             L<Map::Metro::Graph::Line> - Defines a MMG::Line.
247              
248             L<Map::Metro::Graph::LineStation> - Defines a MMG::LineStation.
249              
250             L<Map::Metro::Graph::Route> - Defines a MMG::Route.
251              
252             L<Map::Metro::Graph::Routing> - Defines a MMG::Routing.
253              
254             L<Map::Metro::Graph::Segment> - Defines a MMG::Segment.
255              
256             L<Map::Metro::Graph::Station> - Defines a MMG::Station.
257              
258             L<Map::Metro::Graph::Step> - Defines a MMG::Step.
259              
260             L<Map::Metro::Graph::Transfer> - Defines a MMG::Transfer.
261              
262             =head2 Hierarchy
263              
264             The following is a conceptual overview of the various parts of a graph:
265              
266             At first, the map file is parsed. The four types of blocks (stations, transfers, lines and segments) are translated
267             into their respective object.
268              
269             Next, lines and stations are put together into L<LineStations|Map::Metro::Graph::LineStation>. Every two adjacent LineStations
270             are put into two L<Connections|Map::Metro::Graph::Connection> (one for each direction).
271              
272             Now the network is complete, and it is time to start traversing it.
273              
274             Once a request to search for paths between two stations is given, we first search for the starting L<Station|Map::Metro::Graph::Station> given either a
275             station id or station name. Then we find all L<LineStations|Map::Metro::Graph::LineStation> for that station.
276              
277             Then we do the same for the destination station.
278              
279             And then we walk through the network, from L<LineStation|Map::Metro::Graph::LineStation> to L<LineStation|Map::Metro::Graph::LineStation>, finding their L<Connections|Map::Metro::Graph::Connection>
280             and turning them into L<Steps|Map::Metro::Graph::Step>, which we then add to the L<Route|Map::Metro::Graph::Route>.
281              
282             All L<Routes|Map::Metro::Graph::Route> between the two L<Stations|Map::Metro::Graph::Station> are then put into a L<Routing|Map::Metro::Graph::Routing>, which is returned to the user.
283              
284             =head1 PERFORMANCE
285              
286             Since 0.2200 performance is less than an issue than it used to be, but it can still be improved. Prior to this version the entire network was analyzed up-front. This is unnecessary when searching one (or a few) routes. For long-running applications it is still possible to pre-calculate all paths, see L<asps|Map::Metro::Graph/"asps()">.
287              
288             =head1 STATUS
289              
290             This is somewhat experimental. I don't expect that the map file format will I<break>, but it might be
291             extended. Only the documented api should be relied on, though breaking changes might occur.
292              
293             For all maps in the Map::Metro::Plugin::Map namespace (unless noted):
294              
295             * These maps are not an official source. Use accordingly.
296              
297             * There should be a note regarding what routes the map covers.
298              
299             =head1 COMPATIBILITY
300              
301             Currently requires Perl 5.16.
302              
303             =head1 Map::Metro or Map::Tube?
304              
305             L<Map::Tube> is the main alternative to C<Map::Metro>. They both have their strong and weak points.
306              
307             * Map::Tube is faster.
308              
309             * Map::Tube is more stable: It has been on Cpan for a long time, and is under active development.
310              
311             * Map::Metro has (in my opinion) a better map format.
312              
313             * Map::Metro supports eg. transfers between stations.
314              
315             * See L<Task::MapMetro::Maps> and L<Task::Map::Tube> for available maps.
316              
317             * It is possible to convert Map::Metro maps into Map::Tube maps using L<map-metro.pl|Map::Metro::Cmd/"map-metro.pl metro_to_tube $city">.
318              
319             =head1 SEE ALSO
320              
321             L<Map::Tube>
322              
323             =head1 SOURCE
324              
325             L<https://github.com/Csson/p5-Map-Metro>
326              
327             =head1 HOMEPAGE
328              
329             L<https://metacpan.org/release/Map-Metro>
330              
331             =head1 AUTHOR
332              
333             Erik Carlsson <info@code301.com>
334              
335             =head1 COPYRIGHT AND LICENSE
336              
337             This software is copyright (c) 2016 by Erik Carlsson.
338              
339             This is free software; you can redistribute it and/or modify it under
340             the same terms as the Perl 5 programming language system itself.
341              
342             =cut