File Coverage

blib/lib/Map/Metro.pm
Criterion Covered Total %
statement 37 55 67.2
branch 2 8 25.0
condition n/a
subroutine 12 16 75.0
pod 2 4 50.0
total 53 83 63.8


line stmt bran cond sub pod time code
1 2     2   56782 use 5.10.0;
  2         6  
2 2     2   10 use strict;
  2         1  
  2         48  
3 2     2   9 use warnings;
  2         4  
  2         123  
4              
5             package Map::Metro;
6              
7             # ABSTRACT: Public transport graphing
8             our $AUTHORITY = 'cpan:CSSON'; # AUTHORITY
9             our $VERSION = '0.2404';
10              
11 2     2   787 use Map::Metro::Elk;
  2         4  
  2         8  
12 2     2   3693 use Module::Pluggable search_path => ['Map::Metro::Plugin::Map'], require => 1, sub_name => 'system_maps';
  2         11658  
  2         12  
13 2     2   1085 use Types::Standard qw/ArrayRef Str/;
  2         85230  
  2         15  
14 2     2   1134 use Try::Tiny;
  2         3  
  2         96  
15 2     2   7 use List::Util qw/any/;
  2         3  
  2         91  
16              
17 2     2   847 use Map::Metro::Graph;
  2         6  
  2         71  
18 2     2   14 use Map::Metro::Exceptions;
  2         3  
  2         1397  
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 1     1 0 795 my $self = shift;
83 1         2 my @args = @_;
84              
85 1 50       29 if($self->has_map) {
86 1         6 my @system_maps = map { s{^Map::Metro::Plugin::Map::}{}; $_ } $self->system_maps;
  0         0  
  0         0  
87 1 50   0   305 if(any { $_ eq $self->get_map(0) } @system_maps) {
  0         0  
88 0         0 my $mapclass = 'Map::Metro::Plugin::Map::'.$self->get_map(0);
89 0         0 my $mapobj = $mapclass->new(hooks => $self->hooks);
90 0         0 $self->add_mapclass($mapobj);
91             }
92             else {
93 1     1   7 try { die no_such_map mapname => $self->get_map(0) } catch { die $_->desc };
  1         75  
  1         1057  
94             }
95             }
96             }
97              
98             # Borrowed from Mojo::Util
99             sub decamelize {
100 0     0 0   my $self = shift;
101 0           my $string = shift;
102              
103 0 0         return $string if $string !~ m{[A-Z]};
104             return join '_' => map {
105 0           join ('_' => map { lc } grep { length } split m{([A-Z]{1}[^A-Z]*)})
  0            
  0            
  0            
106             } split '::' => $string;
107             }
108              
109             sub parse {
110 0     0 1   my $self = shift;
111 0           my %args = @_;
112              
113             return Map::Metro::Graph->new(filepath => $self->get_mapclass(0)->maplocation,
114             do_undiacritic => $self->get_mapclass(0)->do_undiacritic,
115             wanted_hook_plugins => [$self->all_hooks],
116 0 0         exists $args{'override_line_change_weight'} ? (override_line_change_weight => $args{'override_line_change_weight'}) : (),
117             )->parse;
118             }
119              
120             sub available_maps {
121 0     0 1   my $self = shift;
122 0           return sort $self->system_maps;
123             }
124              
125             1;
126              
127             __END__
128              
129             =pod
130              
131             =encoding utf-8
132              
133             =head1 NAME
134              
135             Map::Metro - Public transport graphing
136              
137              
138              
139             =begin html
140              
141             <p>
142             <img src="https://img.shields.io/badge/perl-5.10+-blue.svg" alt="Requires Perl 5.10+" />
143             <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>
144             <a href="http://cpants.cpanauthors.org/dist/Map-Metro-0.2404"><img src="https://badgedepot.code301.com/badge/kwalitee/Map-Metro/0.2404" alt="Distribution kwalitee" /></a>
145             <a href="http://matrix.cpantesters.org/?dist=Map-Metro%200.2404"><img src="https://badgedepot.code301.com/badge/cpantesters/Map-Metro/0.2404" alt="CPAN Testers result" /></a>
146             <img src="https://img.shields.io/badge/coverage-72.9%-red.svg" alt="coverage 72.9%" />
147             </p>
148              
149             =end html
150              
151             =head1 VERSION
152              
153             Version 0.2404, released 2016-04-30.
154              
155             =head1 SYNOPSIS
156              
157             # Install a map
158             $ cpanm Map::Metro::Plugin::Map::Stockholm
159              
160             # And then
161             my $graph = Map::Metro->new('Stockholm', hooks => ['PrettyPrinter'])->parse;
162              
163             my $routing = $graph->routing_for('Universitetet', 'Kista');
164              
165             # or in a terminal
166             $ map-metro.pl route Stockholm Universitetet Kista
167              
168             prints
169              
170             From Universitetet to Kista
171             ===========================
172              
173             -- Route 1 (cost 15) ----------
174             [ T14 ] Universitetet
175             [ T14 ] Tekniska högskolan
176             [ T14 ] Stadion
177             [ T14 ] Östermalmstorg
178             [ T14 ] T-Centralen
179             [ * T11 ] T-Centralen
180             [ T11 ] RÃ¥dhuset
181             [ T11 ] Fridhemsplan
182             [ T11 ] Stadshagen
183             [ T11 ] Västra skogen
184             [ T11 ] Solna centrum
185             [ T11 ] Näckrosen
186             [ T11 ] Hallonbergen
187             [ T11 ] Kista
188              
189             T11 Blue line
190             T14 Red line
191              
192             *: Transfer to other line
193             +: Transfer to other station
194              
195             =head1 DESCRIPTION
196              
197             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.
198              
199             See L<Task::MapMetro::Maps> for a list of released maps.
200              
201             =head2 Methods
202              
203             =head3 new($city, hooks => [])
204              
205             B<C<$city>>
206              
207             The name of the city you want to search connections in. Mandatory, unless you are only going to call L</"available_maps">.
208              
209             B<C<$hooks>>
210              
211             Array reference of L<Hooks|Map::Metro::Hook> that listens for events.
212              
213             =head3 parse()
214              
215             Returns a L<Map::Metro::Graph> object containing the entire graph.
216              
217             =head3 available_maps()
218              
219             Returns an array reference containing the names of all Map::Metro maps installed on the system.
220              
221             =head2 What is a unique path?
222              
223             The following rules are a guideline:
224              
225             If the starting station and finishing station...
226              
227             ...are on the same line there will be no changes to other lines.
228              
229             ...shares multiple lines (e.g., both stations are on both line 2 and 4), each line constitutes a route.
230              
231             ...are on different lines, line changes will take place at suitable station(s). There is no guarantee that the same stations will be chosen for line changes between searches, if there are more than one suitable station to make a change at.
232              
233             =head1 MORE INFORMATION
234              
235             L<Map::Metro::Graph> - How to use graph object.
236              
237             L<Map::Metro::Plugin::Map> - How to make your own maps.
238              
239             L<Map::Metro::Hook> - How to extend Map::Metro via hooks/events.
240              
241             L<Map::Metro::Cmd> - A guide to the command line application.
242              
243             L<Map::Metro::Graph::Connection> - Defines a MMG::Connection.
244              
245             L<Map::Metro::Graph::Line> - Defines a MMG::Line.
246              
247             L<Map::Metro::Graph::LineStation> - Defines a MMG::LineStation.
248              
249             L<Map::Metro::Graph::Route> - Defines a MMG::Route.
250              
251             L<Map::Metro::Graph::Routing> - Defines a MMG::Routing.
252              
253             L<Map::Metro::Graph::Segment> - Defines a MMG::Segment.
254              
255             L<Map::Metro::Graph::Station> - Defines a MMG::Station.
256              
257             L<Map::Metro::Graph::Step> - Defines a MMG::Step.
258              
259             L<Map::Metro::Graph::Transfer> - Defines a MMG::Transfer.
260              
261             =head2 Hierarchy
262              
263             The following is a conceptual overview of the various parts of a graph:
264              
265             At first, the map file is parsed. The four types of information (stations, transfers, lines and segments) are translated
266             into their respective objects.
267              
268             Next, lines and stations are put together into L<LineStations|Map::Metro::Graph::LineStation>. Every two adjacent LineStations
269             are put into two L<Connections|Map::Metro::Graph::Connection> (one for each direction).
270              
271             Now the network is complete, and it is time to start traversing it.
272              
273             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
274             station id or station name. Then we find all L<LineStations|Map::Metro::Graph::LineStation> for that station.
275              
276             Then we do the same for the destination station.
277              
278             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>
279             and turning them into L<Steps|Map::Metro::Graph::Step>, which we then add to the L<Route|Map::Metro::Graph::Route>.
280              
281             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.
282              
283             =head1 PERFORMANCE
284              
285             Since 0.2200 performance is less of an issue than it used to be, but it could still be improved. Prior to that 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()">.
286              
287             It is also possible to run the backend to some commands in a server, see L<App::Map::Metro>.
288              
289             =head1 STATUS
290              
291             This is somewhat experimental. I don't expect that the map file format will I<break>, but it might be
292             extended. Only the documented api should be used, though breaking changes might occur.
293              
294             For all maps in the Map::Metro::Plugin::Map namespace (unless noted):
295              
296             =over 4
297              
298             =item *
299              
300             These maps are not an official source. Use accordingly.
301              
302             =item *
303              
304             There should be a note regarding what routes the map covers.
305              
306             =back
307              
308             =head1 COMPATIBILITY
309              
310             Under Perl 5.16 or greater, C<fc> will be used instead of C<lc> for some string comparisons. Depending on the map definition
311             this might lead to some maps not working properly on pre-5.16 Perls.
312              
313             Prior to version 0.2400, C<Map::Metro> required at least Perl 5.16.
314              
315             =head1 Map::Metro or Map::Tube?
316              
317             L<Map::Tube> is the main alternative to C<Map::Metro>. They both have their strong and weak points.
318              
319             =over 4
320              
321             =item *
322              
323             Map::Tube is faster.
324              
325             =item *
326              
327             Map::Tube is more stable: It has been on Cpan for a long time, and is under active development.
328              
329             =item *
330              
331             Map::Metro has (in my opinion) a better map format.
332              
333             =item *
334              
335             Map::Metro supports eg. transfers between stations.
336              
337             =item *
338              
339             See L<Task::MapMetro::Maps> and L<Task::Map::Tube> for available maps.
340              
341             =item *
342              
343             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">.
344              
345             =back
346              
347             =head1 SEE ALSO
348              
349             =over 4
350              
351             =item *
352              
353             L<Task::MapMetro::Maps> - Available maps
354              
355             =item *
356              
357             L<Map::Tube> - An alternative
358              
359             =back
360              
361             =head1 SOURCE
362              
363             L<https://github.com/Csson/p5-Map-Metro>
364              
365             =head1 HOMEPAGE
366              
367             L<https://metacpan.org/release/Map-Metro>
368              
369             =head1 AUTHOR
370              
371             Erik Carlsson <info@code301.com>
372              
373             =head1 COPYRIGHT AND LICENSE
374              
375             This software is copyright (c) 2016 by Erik Carlsson.
376              
377             This is free software; you can redistribute it and/or modify it under
378             the same terms as the Perl 5 programming language system itself.
379              
380             =cut