$Map::Tube::VERSION
=
'3.97'
;
$Map::Tube::AUTHORITY
=
'cpan:MANWAR'
;
Hide Show 10 lines of Pod
use
5.006;
use
Map::Tube::Utils
qw(to_perl is_same trim common_lines get_method_map is_valid_color)
;
Hide Show 51 lines of Pod
has
[
qw(name name_to_id plugins _active_link _other_links _line_stations _line_station_index _common_lines)
] => (
is
=>
'rw'
);
has
experimental
=> (
is
=>
'ro'
,
default
=>
sub
{ 0 });
has
nodes
=> (
is
=>
'rw'
,
isa
=> NodeMap);
has
lines
=> (
is
=>
'rw'
,
isa
=> Lines );
has
tables
=> (
is
=>
'rw'
,
isa
=> Tables );
has
routes
=> (
is
=>
'rw'
,
isa
=> Routes );
has
_lines
=> (
is
=>
'rw'
,
isa
=> LineMap);
has
bgcolor
=> (
is
=>
'rw'
,
isa
=> Color );
our
$AUTOLOAD
;
our
$PLUGINS
= {
'Map::Tube::Plugin::Graph'
=> 1,
'Map::Tube::Plugin::FuzzyFind'
=> 1,
};
sub
AUTOLOAD {
my
$name
=
$AUTOLOAD
;
$name
=~ s/.*://;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
my
$method_map
= get_method_map();
if
(
exists
$method_map
->{
$name
}) {
my
$module
=
$method_map
->{
$name
}->{module};
my
$exception
=
$method_map
->{
$name
}->{exception};
$exception
->throw({
method
=>
"${module}::${name}"
,
message
=>
"ERROR: Missing plugin $module."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] });
}
}
sub
BUILD {
my
(
$self
) =
@_
;
my
@attributes
= (
keys
%{Moo->_constructor_maker_for(
ref
(
$self
))->all_attribute_specs});
unless
((
grep
/^xml$/,
@attributes
) || (
grep
/^json$/,
@attributes
)) {
die
"ERROR: Can't apply Map::Tube role, missing 'xml' or 'json'."
;
}
$self
->_init_map;
$self
->_load_plugins;
}
Hide Show 35 lines of Pod
sub
get_shortest_route {
my
(
$self
,
$from
,
$to
) =
@_
;
(
$from
,
$to
) =
$self
->_validate_input(
'get_shortest_route'
,
$from
,
$to
);
my
$_from
=
$self
->get_node_by_id(
$from
);
my
$_to
=
$self
->get_node_by_id(
$to
);
$self
->_capture_common_lines(
$_from
,
$_to
);
my
$reverse
= 0;
if
(@{
$self
->{_common_lines}}) {
my
$_common_line
=
$self
->{_common_lines}->[0];
my
$from_index
=
$self
->{_line_station_index}
->{
uc
(
$_common_line
)}
->{
$_from
->id};
my
$to_index
=
$self
->{_line_station_index}
->{
uc
(
$_common_line
)}
->{
$_to
->id};
}
if
(
$reverse
) {
(
$from
,
$to
) = (
$to
,
$from
);
(
$_from
,
$_to
) = (
$_to
,
$_from
);
}
$self
->_get_shortest_route(
$from
);
my
$nodes
= [];
while
(
defined
(
$to
) && !(is_same(
$from
,
$to
))) {
push
@$nodes
,
$self
->get_node_by_id(
$to
);
$to
=
$self
->_get_path(
$to
);
}
push
@$nodes
,
$_from
;
my
$_nodes
;
if
(
$reverse
) {
$_nodes
= [
@$nodes
];
}
else
{
$_nodes
= [
reverse
(
@$nodes
) ];
}
my
$start
=
$_nodes
->[0];
my
$end
=
$_nodes
->[-1];
if
(
scalar
(
@$_nodes
) == 2) {
my
$start_name
=
$start
->name;
my
$end_name
=
$end
->name;
unless
(
$self
->_is_directly_linked(
$start_name
,
$end_name
)) {
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::RouteNotFound->throw({
method
=> __PACKAGE__.
"::get_shortest_route"
,
message
=>
"ERROR: Route not found from [$start_name] to [$end_name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] });
}
}
return
Map::Tube::Route->new(
{
from
=>
$start
,
to
=>
$end
,
nodes
=>
$_nodes
,
}
);
}
Hide Show 21 lines of Pod
sub
get_all_routes {
my
(
$self
,
$from
,
$to
) =
@_
;
(
$from
,
$to
) =
$self
->_validate_input(
'get_all_routes'
,
$from
,
$to
);
return
$self
->_get_all_routes([
$from
],
$to
);
}
Hide Show 10 lines of Pod
sub
get_node_by_id {
my
(
$self
,
$id
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::MissingStationId->throw({
method
=> __PACKAGE__.
"::get_node_by_id"
,
message
=>
"ERROR: Missing Station ID."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$id
;
my
$node
=
$self
->{nodes}->{
$id
};
Map::Tube::Exception::InvalidStationId->throw({
method
=> __PACKAGE__.
"::get_node_by_id"
,
message
=>
"ERROR: Invalid Station ID [$id]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$node
;
my
@nodes
=
$self
->_get_node_id(
$node
->name);
return
$node
if
(
scalar
(
@nodes
) == 1);
my
$lines
= {};
foreach
my
$l
(@{
$node
->line}) {
$lines
->{
$l
->name} =
$l
if
defined
$l
->name;
}
foreach
my
$i
(
@nodes
) {
foreach
my
$j
(@{
$self
->{nodes}->{
$i
}->line}) {
$lines
->{
$j
->name} =
$j
if
defined
$j
->name;
}
}
$node
->line([
values
%$lines
]);
return
$node
;
}
Hide Show 6 lines of Pod
sub
get_node_by_name {
my
(
$self
,
$name
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::MissingStationName->throw({
method
=> __PACKAGE__.
"::get_node_by_name"
,
message
=>
"ERROR: Missing Station Name."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$name
;
my
$id
=
$self
->_get_node_id(
$name
);
Map::Tube::Exception::InvalidStationName->throw({
method
=> __PACKAGE__.
"::get_node_by_name"
,
message
=>
"ERROR: Invalid Station Name [$name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$id
;
return
$self
->get_node_by_id(
$id
);
}
Hide Show 6 lines of Pod
sub
get_line_by_id {
my
(
$self
,
$id
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::MissingLineId->throw({
method
=> __PACKAGE__.
"::get_line_by_id"
,
message
=>
"ERROR: Missing Line ID."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$id
;
my
$line
=
$self
->_get_line_object_by_id(
$id
);
Map::Tube::Exception::InvalidLineId->throw({
method
=> __PACKAGE__.
"::get_line_by_id"
,
message
=>
"ERROR: Invalid Line ID [$id]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$line
;
return
$line
;
}
Hide Show 6 lines of Pod
sub
get_line_by_name {
my
(
$self
,
$name
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::MissingLineName->throw({
method
=> __PACKAGE__.
"::get_line_by_name"
,
message
=>
"ERROR: Missing Line Name."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$name
;
my
$line
=
$self
->_get_line_object_by_name(
$name
);
Map::Tube::Exception::InvalidLineName->throw({
method
=> __PACKAGE__.
"::get_line_by_name"
,
message
=>
"ERROR: Invalid Line Name [$name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$line
;
return
$line
;
}
Hide Show 6 lines of Pod
sub
get_lines {
my
(
$self
) =
@_
;
my
$lines
= [];
my
$other_links
=
$self
->_other_links;
foreach
(@{
$self
->{lines}}) {
next
if
exists
$other_links
->{
uc
(
$_
->id)};
push
@$lines
,
$_
if
defined
$_
->name;
}
return
$lines
;
}
Hide Show 7 lines of Pod
sub
get_stations {
my
(
$self
,
$line_name
) =
@_
;
my
$lines
= [];
my
$stations
= [];
my
$seen
= {};
if
(
defined
$line_name
) {
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
my
$line
=
$self
->_get_line_object_by_name(
$line_name
);
Map::Tube::Exception::InvalidLineName->throw({
method
=> __PACKAGE__.
"::get_stations"
,
message
=>
"ERROR: Invalid Line Name [$line_name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$line
;
$lines
= [
$self
->_get_line_object_by_name(
$line_name
) ];
}
else
{
$lines
=
$self
->get_lines;
}
foreach
my
$line
(
@$lines
) {
foreach
my
$station
(@{
$line
->{stations}}) {
unless
(
exists
$seen
->{
$station
->id}) {
push
@$stations
,
$self
->get_node_by_id(
$station
->id);
$seen
->{
$station
->id} = 1;
}
}
}
return
$stations
;
}
Hide Show 7 lines of Pod
sub
get_next_stations {
my
(
$self
,
$station_name
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::MissingStationName->throw({
method
=> __PACKAGE__.
"::get_next_stations"
,
message
=>
"ERROR: Missing Station Name."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$station_name
;
my
$node_id
=
$self
->_get_node_id(
$station_name
);
Map::Tube::Exception::InvalidStationName->throw({
method
=> __PACKAGE__.
"::get_next_stations"
,
message
=>
"ERROR: Invalid Station Name [$station_name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
defined
$node_id
;
my
$nodes
= [];
my
$node
=
$self
->get_node_by_id(
$node_id
);
foreach
my
$link
(
split
/,/,
$node
->{
link
}) {
push
@$nodes
,
$self
->get_node_by_id(
$link
);
}
return
$nodes
;
}
Hide Show 6 lines of Pod
sub
get_linked_stations {
my
(
$self
,
$station_name
) =
@_
;
my
$nodes
=
$self
->get_next_stations(
$station_name
);
my
$linked_stations
= [];
foreach
my
$node
(
@$nodes
) {
push
@$linked_stations
,
$node
->name;
}
return
$linked_stations
;
}
Hide Show 9 lines of Pod
sub
get_map_data {
my
(
$self
,
$caller
,
$method
) =
@_
;
my
$data
;
my
$xml
=
$self
->xml;
if
(
$xml
ne
''
) {
eval
{
$data
= XML::Twig->new->parsefile(
$xml
)->simplify(
keyattr
=>
'stations'
,
forcearray
=> 0);
my
$lines
=
$data
->{lines}->{line};
if
(
ref
(
$lines
) eq
'HASH'
) {
$data
->{lines}->{line} = [
$lines
];
}
};
unless
($@) {
$self
->_validate_map_structure(
$data
,
$caller
);
return
$data
;
}
$@ =~ s/\s+at\s+\S*?\.pm\s+line\s+.*$//;
Map::Tube::Exception::MalformedMapData->throw({
method
=>
$method
,
message
=>
"ERROR: Malformed Map Data ($xml): $@"
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
else
{
my
$json
=
$self
->json;
if
(
$json
ne
''
) {
eval
{
$data
= to_perl(
$json
) };
unless
($@) {
$self
->_validate_map_structure(
$data
,
$caller
);
return
$data
;
}
$@ =~ s/\s+at\s+\S*?\.pm\s+line\s+.*$//;
Map::Tube::Exception::MalformedMapData->throw({
method
=>
$method
,
message
=>
"ERROR: Malformed Map Data ($json): $@"
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
else
{
if
(!
defined
$caller
) {
$method
= __PACKAGE__.
'::get_map_data'
;
my
@_caller
=
caller
(0);
@_caller
=
caller
(2)
if
$_caller
[3] eq
'(eval)'
;
$caller
= \
@_caller
;
}
Map::Tube::Exception::MissingMapData->throw({
method
=>
$method
,
message
=>
"ERROR: Missing Map Data."
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
}
}
Hide Show 180 lines of Pod
sub
_get_shortest_route {
my
(
$self
,
$from
) =
@_
;
my
$nodes
= [];
my
$index
= 0;
my
$seen
= {};
$self
->_init_table;
$self
->_set_length(
$from
,
$index
);
$self
->_set_path(
$from
,
$from
);
my
$all_nodes
=
$self
->{nodes};
while
(
defined
(
$from
)) {
my
$length
=
$self
->_get_length(
$from
);
my
$f_node
=
$all_nodes
->{
$from
};
$self
->_set_active_links(
$f_node
);
if
(
defined
$f_node
) {
my
$links
= [
split
/\,/,
$f_node
->{
link
} ];
while
(
scalar
(
@$links
) > 0) {
my
(
$success
,
$link
) =
$self
->_get_next_link(
$from
,
$seen
,
$links
);
$success
or (
$links
= [
grep
(!/\b\Q
$link
\E\b/,
@$links
) ]) and
next
;
if
((
$self
->_get_length(
$link
) == 0) || (
$length
> (
$index
+ 1))) {
$self
->_set_length(
$link
,
$length
+ 1);
$self
->_set_path(
$link
,
$from
);
push
@$nodes
,
$link
;
}
$seen
->{
$link
} = 1;
$links
= [
grep
(!/\b\Q
$link
\E\b/,
@$links
) ];
}
}
$index
=
$length
+ 1;
$from
=
shift
@$nodes
;
$nodes
= [
grep
(!/\b\Q
$from
\E\b/,
@$nodes
) ]
if
defined
$from
;
}
}
sub
_get_all_routes {
my
(
$self
,
$visited
,
$to
) =
@_
;
my
$last
=
$visited
->[-1];
my
$nodes
=
$self
->get_node_by_id(
$last
)->
link
;
foreach
my
$id
(
split
/\,/,
$nodes
) {
next
if
_is_visited(
$id
,
$visited
);
if
(is_same(
$id
,
$to
)) {
push
@$visited
,
$id
;
$self
->_set_routes(
$visited
);
pop
@$visited
;
last
;
}
}
foreach
my
$id
(
split
/\,/,
$nodes
) {
next
if
(_is_visited(
$id
,
$visited
) || is_same(
$id
,
$to
));
push
@$visited
,
$id
;
$self
->_get_all_routes(
$visited
,
$to
);
pop
@$visited
;
}
return
$self
->{routes};
}
sub
_map_node_name {
my
(
$self
,
$name
,
$id
) =
@_
;
$self
->{name_to_id}->{
uc
(
$name
)} =
$id
;
}
sub
_validate_input {
my
(
$self
,
$method
,
$from
,
$to
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
Map::Tube::Exception::MissingStationName->throw({
method
=> __PACKAGE__.
"::$method"
,
message
=>
"ERROR: Missing Station Name."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
unless
(
defined
(
$from
) &&
defined
(
$to
));
$from
= trim(
$from
);
my
$_from
=
$self
->get_node_by_name(
$from
);
$to
= trim(
$to
);
my
$_to
=
$self
->get_node_by_name(
$to
);
return
(
$_from
->{id},
$_to
->{id});
}
sub
_xml_data {
my
(
$self
) =
@_
;
return
$self
->get_map_data;
}
sub
_init_map {
my
(
$self
) =
@_
;
my
$_lines
= {};
my
$lines
= {};
my
$nodes
= {};
my
$tables
= {};
my
$_other_links
= {};
my
$_seen_nodes
= {};
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
my
$method
= __PACKAGE__.
"::_init_map"
;
my
$data
=
$self
->get_map_data(\
@caller
,
$method
);
$self
->{name} =
$data
->{name};
my
@lines
;
if
(
exists
$data
->{lines} &&
exists
$data
->{lines}->{line}) {
@lines
= (
ref
$data
->{lines}->{line} eq
'HASH'
)
? (
$data
->{lines}->{line})
: @{
$data
->{lines}->{line}};
}
my
$master_line_data
= {};
foreach
(
@lines
) {
$master_line_data
->{
$_
->{id}} = 1;
}
my
$has_station_index
= {};
foreach
my
$station
(@{
$data
->{stations}->{station}}) {
my
$id
=
$station
->{id};
Map::Tube::Exception::DuplicateStationId->throw({
method
=>
$method
,
message
=>
"ERROR: Duplicate Station ID [$id]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
if
(
exists
$_seen_nodes
->{
$id
});
$_seen_nodes
->{
$id
} = 1;
my
$name
=
$station
->{name};
Map::Tube::Exception::DuplicateStationName->throw({
method
=>
$method
,
message
=>
"ERROR: Duplicate Station Name [$name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
if
(
defined
$self
->{name_to_id}->{
uc
(
$name
)});
$self
->_map_node_name(
$name
,
$id
);
$tables
->{
$id
} = Map::Tube::Table->new({
id
=>
$id
});
my
$_station_lines
= [];
foreach
my
$_line
(
split
/\,/,
$station
->{line}) {
if
(
$_line
=~ /\:/) {
$_line
=
$self
->_capture_line_station(
$_line
,
$id
);
$has_station_index
->{
$_line
} = 1;
}
if
(!
exists
$master_line_data
->{
$_line
}) {
Map::Tube::Exception::InvalidStationLineId->throw({
method
=>
$method
,
message
=>
"ERROR: Invalid line [$_line] for station [$name]."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] });
}
my
$uc_line
=
uc
(
$_line
);
my
$line
=
$lines
->{
$uc_line
};
$line
= Map::Tube::Line->new({
id
=>
$_line
})
unless
defined
$line
;
$_lines
->{
$uc_line
} =
$line
;
$lines
->{
$uc_line
} =
$line
;
push
@$_station_lines
,
$line
;
}
if
(
exists
$station
->{other_link} &&
defined
$station
->{other_link}) {
my
@link_nodes
= ();
foreach
my
$_entry
(
split
/\,/,
$station
->{other_link}) {
my
(
$_link_type
,
$_nodes
) =
split
/\:/,
$_entry
, 2;
my
$uc_link_type
=
uc
(
$_link_type
);
my
$line
=
$lines
->{
$uc_link_type
};
$line
= Map::Tube::Line->new({
id
=>
$_link_type
,
name
=>
$_link_type
})
unless
defined
$line
;
$_lines
->{
$uc_link_type
} =
$line
;
$lines
->{
$uc_link_type
} =
$line
;
$_other_links
->{
$uc_link_type
} = 1;
push
@$_station_lines
,
$line
;
push
@link_nodes
, (
split
/\|/,
$_nodes
);
}
$station
->{
link
} .=
","
.
join
(
","
,
@link_nodes
);
}
$station
->{line} =
$_station_lines
;
my
$node
= Map::Tube::Node->new(
$station
);
$nodes
->{
$id
} =
$node
;
foreach
my
$line
(@{
$_station_lines
}) {
next
if
exists
$has_station_index
->{
$line
->id};
push
@{
$line
->{stations}},
$node
;
}
}
foreach
my
$_line
(
@lines
) {
my
$uc_line
=
uc
(
$_line
->{id});
my
$line
=
$_lines
->{
$uc_line
};
if
(
defined
$line
) {
$line
->{name} =
$_line
->{name};
$line
->{color} =
$_line
->{color};
if
(
$has_station_index
) {
foreach
(
sort
{
$a
<=>
$b
}
keys
%{
$self
->{_line_stations}->{
$uc_line
}}) {
my
$station_id
=
$self
->{_line_stations}->{
$uc_line
}->{
$_
};
$line
->add_station(
$nodes
->{
$station_id
});
}
}
$_lines
->{
$uc_line
} =
$line
;
}
}
$self
->_order_station_lines(
$nodes
);
$self
->lines([
values
%$lines
]);
$self
->_lines(
$_lines
);
$self
->_other_links(
$_other_links
);
foreach
my
$node_id
(
keys
%$nodes
) {
my
$node_obj
=
$nodes
->{
$node_id
};
foreach
my
$link
(
split
/\,/,
$node_obj
->{
link
}) {
push
@{
$node_obj
->{links}},
$nodes
->{
$link
};
}
}
$self
->nodes(
$nodes
);
$self
->tables(
$tables
);
}
sub
_is_directly_linked {
my
(
$self
,
$start_station
,
$end_station
) =
@_
;
my
$linked_stations
=
$self
->get_linked_stations(
$start_station
);
return
grep
{
$_
eq
$end_station
}
@$linked_stations
;
}
sub
_init_table {
my
(
$self
) =
@_
;
foreach
my
$id
(
keys
%{
$self
->{tables}}) {
$self
->{tables}->{
$id
}->{path} =
undef
;
$self
->{tables}->{
$id
}->{
length
} = 0;
}
$self
->{_active_links} =
undef
;
}
sub
_load_plugins {
my
(
$self
) =
@_
;
$self
->{plugins} = [ Map::Tube::Pluggable::plugins ];
foreach
my
$plugin
(@{
$self
->plugins}) {
next
unless
(
exists
$PLUGINS
->{
$plugin
});
Role::Tiny->apply_roles_to_object(
$self
,
$plugin
);
}
}
sub
_capture_common_lines {
my
(
$self
,
$from
,
$to
) =
@_
;
my
$from_lines
= [
map
{
$_
->id } @{
$from
->line} ];
my
$to_lines
= [
map
{
$_
->id } @{
$to
->line} ];
$self
->{_common_lines} = [ common_lines(
$from_lines
,
$to_lines
) ];
}
sub
_get_common_lines {
my
(
$nodes
,
$active_links
) =
@_
;
my
%_unique_links
= ();
foreach
(@{
$active_links
}) {
$_unique_links
{
$_
} = 1;
}
my
%_common_lines
= ();
foreach
my
$_link
(
keys
%_unique_links
) {
foreach
my
$_link_line
(@{
$nodes
->{
$_link
}->line}) {
$_common_lines
{
$_link_line
->id} = 1;
}
}
return
(
keys
%_common_lines
);
}
sub
_get_next_link {
my
(
$self
,
$from
,
$seen
,
$links
) =
@_
;
my
$nodes
=
$self
->{nodes};
my
$active_links
=
$self
->{_active_links};
my
@common_lines
= common_lines(
$active_links
->[0],
$active_links
->[1]);
if
(
$self
->{experimental} &&
scalar
(@{
$self
->{_common_lines}})) {
@common_lines
= (@{
$self
->{_common_lines}},
@common_lines
);
}
my
$link
=
undef
;
foreach
my
$_link
(
@$links
) {
return
(0,
$_link
)
if
((
exists
$seen
->{
$_link
}) || (
$from
eq
$_link
));
my
$node
=
$nodes
->{
$_link
};
next
unless
defined
$node
;
my
@lines
= ();
foreach
(@{
$node
->{line}}) {
push
@lines
,
$_
->{id}; }
my
@common
= common_lines(\
@common_lines
, \
@lines
);
return
(1,
$_link
)
if
(
scalar
(
@common
) > 0);
$link
=
$_link
;
}
return
(1,
$link
);
}
sub
_set_active_links {
my
(
$self
,
$node
) =
@_
;
my
$active_links
=
$self
->{_active_links};
my
$links
= [
split
/\,/,
$node
->{
link
} ];
if
(
defined
$active_links
) {
shift
@$active_links
;
push
@$active_links
,
$links
;
}
else
{
push
@$active_links
,
$links
;
push
@$active_links
,
$links
;
}
$self
->{_active_links} =
$active_links
;
}
sub
_validate_map_structure {
my
(
$self
,
$data
,
$caller
) =
@_
;
unless
(
exists
$data
->{lines}
&&
exists
$data
->{lines}->{line}
&& @{
$data
->{lines}->{line}}) {
Map::Tube::Exception::InvalidLineStructure->throw({
method
=> __PACKAGE__.
"::_validate_map_structure"
,
message
=>
"ERROR: Invalid line structure in map data."
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
unless
(
exists
$data
->{stations}
&&
exists
$data
->{stations}->{station}
&& @{
$data
->{stations}->{station}}) {
Map::Tube::Exception::InvalidStationStructure->throw({
method
=> __PACKAGE__.
"::_validate_map_structure"
,
message
=>
"ERROR: Invalid station structure in map data."
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
}
sub
_validate_map_data {
my
(
$self
) =
@_
;
my
@caller
=
caller
(0);
@caller
=
caller
(2)
if
$caller
[3] eq
'(eval)'
;
my
$nodes
=
$self
->{nodes};
my
$seen
= {};
$self
->_validate_lines(\
@caller
);
foreach
my
$id
(
keys
%$nodes
) {
Map::Tube::Exception::InvalidStationId->throw({
method
=> __PACKAGE__.
"::_validate_map_data"
,
message
=>
"ERROR: Station ID can't have ',' character."
,
filename
=>
$caller
[1],
line_number
=>
$caller
[2] })
if
(
$id
=~ /\,/);
my
$node
=
$nodes
->{
$id
};
$self
->_validate_nodes(\
@caller
,
$nodes
,
$node
,
$seen
);
$self
->_validate_self_linked_nodes(\
@caller
,
$node
,
$id
);
$self
->_validate_multi_linked_nodes(\
@caller
,
$node
,
$id
);
$self
->_validate_multi_lined_nodes(\
@caller
,
$node
,
$id
);
}
}
sub
_validate_lines {
my
(
$self
,
$caller
) =
@_
;
my
$lines
=
$self
->{lines};
foreach
(
@$lines
) {
my
$line_color
=
$_
->{color};
if
(
defined
$line_color
&& !(is_valid_color(
$line_color
))) {
Map::Tube::Exception::InvalidLineColor->throw({
method
=> __PACKAGE__.
"::_validate_map_data"
,
message
=>
"ERROR: Invalid Line Color [$line_color]."
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
}
}
sub
_validate_nodes {
my
(
$self
,
$caller
,
$nodes
,
$node
,
$seen
) =
@_
;
foreach
(
split
/\,/,
$node
->{
link
}) {
next
if
(
exists
$seen
->{
$_
});
my
$_node
=
$nodes
->{
$_
};
Map::Tube::Exception::InvalidStationId->throw({
method
=> __PACKAGE__.
"::_validate_map_data"
,
message
=>
"ERROR: Invalid Station ID [$_]."
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] })
unless
(
defined
$_node
);
$seen
->{
$_
} = 1;
}
}
sub
_validate_self_linked_nodes {
my
(
$self
,
$caller
,
$node
,
$id
) =
@_
;
if
(
grep
{
$_
eq
$id
} (
split
/\,/,
$node
->{
link
})) {
Map::Tube::Exception::FoundSelfLinkedStation->throw({
method
=> __PACKAGE__.
"::_validate_map_data"
,
message
=>
sprintf
(
"ERROR: %s is self linked,"
,
$id
),
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
}
sub
_validate_multi_linked_nodes {
my
(
$self
,
$caller
,
$node
,
$id
) =
@_
;
my
%links
= ();
my
$max_link
= 1;
foreach
my
$link
(
split
( /\,/,
$node
->{
link
})) {
$links
{
$link
}++;
}
foreach
(
keys
%links
) {
$max_link
=
$links
{
$_
}
if
(
$max_link
<
$links
{
$_
});
}
if
(
$max_link
> 1) {
my
$message
=
sprintf
(
"ERROR: %s linked to %s multiple times,"
,
$id
,
join
(
','
,
grep
{
$links
{
$_
} > 1 }
keys
%links
));
Map::Tube::Exception::FoundMultiLinkedStation->throw({
method
=> __PACKAGE__.
"::_validate_map_data"
,
message
=>
$message
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
}
sub
_capture_line_station {
my
(
$self
,
$line
,
$station_id
) =
@_
;
my
(
$line_id
,
$sequence
) =
split
/\:/,
$line
, 2;
$self
->{_line_stations}->{
uc
(
$line_id
)}->{
$sequence
} =
$station_id
;
$self
->{_line_station_index}->{
uc
(
$line_id
)}->{
$station_id
} =
$sequence
;
return
$line_id
;
}
sub
_validate_multi_lined_nodes {
my
(
$self
,
$caller
,
$node
,
$id
) =
@_
;
my
%lines
= ();
foreach
(@{
$node
->{line}}) {
$lines
{
$_
->{id}}++; }
my
$max_link
= 1;
foreach
(
keys
%lines
) {
$max_link
=
$lines
{
$_
}
if
(
$max_link
<
$lines
{
$_
});
}
if
(
$max_link
> 1) {
my
$message
=
sprintf
(
"ERROR: %s has multiple lines %s,"
,
$id
,
join
(
','
,
grep
{
$lines
{
$_
} > 1 }
keys
%lines
));
Map::Tube::Exception::FoundMultiLinedStation->throw({
method
=> __PACKAGE__.
"::_validate_map_data"
,
message
=>
$message
,
filename
=>
$caller
->[1],
line_number
=>
$caller
->[2] });
}
}
sub
_set_routes {
my
(
$self
,
$routes
) =
@_
;
my
$_routes
= [];
my
$nodes
=
$self
->{nodes};
foreach
my
$id
(
@$routes
) {
push
@$_routes
,
$nodes
->{
$id
};
}
my
$from
=
$_routes
->[0];
my
$to
=
$_routes
->[-1];
my
$route
= Map::Tube::Route->new({
from
=>
$from
,
to
=>
$to
,
nodes
=>
$_routes
});
push
@{
$self
->{routes}},
$route
;
}
sub
_get_path {
my
(
$self
,
$id
) =
@_
;
return
$self
->{tables}->{
$id
}->{path};
}
sub
_set_path {
my
(
$self
,
$id
,
$node_id
) =
@_
;
return
unless
(
defined
$id
&&
defined
$node_id
);
$self
->{tables}->{
$id
}->{path} =
$node_id
;
}
sub
_get_length {
my
(
$self
,
$id
) =
@_
;
return
0
unless
(
defined
$id
&&
defined
$self
->{tables}->{
$id
});
return
$self
->{tables}->{
$id
}->{
length
};
}
sub
_set_length {
my
(
$self
,
$id
,
$value
) =
@_
;
return
unless
(
defined
$id
&&
defined
$value
);
$self
->{tables}->{
$id
}->{
length
} =
$value
;
}
sub
_get_table {
my
(
$self
,
$id
) =
@_
;
return
$self
->{tables}->{
$id
};
}
sub
_get_node_id {
my
(
$self
,
$name
) =
@_
;
return
unless
defined
$name
;
$name
=~ s/^\s+//;
$name
=~ s/\s+$//;
return
$self
->{name_to_id}->{
uc
(
$name
)};
}
sub
_get_line_object_by_name {
my
(
$self
,
$name
) =
@_
;
$name
=
uc
(
$name
);
foreach
my
$line_id
(
keys
%{
$self
->{_lines}}) {
my
$line
=
$self
->{_lines}->{
$line_id
};
if
(
defined
$line
&&
defined
$line
->name) {
return
$line
if
(
$name
eq
uc
(
$line
->name));
}
}
return
;
}
sub
_get_line_object_by_id {
my
(
$self
,
$id
) =
@_
;
$id
=
uc
(
$id
);
foreach
my
$line_id
(
keys
%{
$self
->{_lines}}) {
my
$line
=
$self
->{_lines}->{
$line_id
};
if
(
defined
$line
&&
defined
$line
->name) {
return
$line
if
(
$id
eq
uc
(
$line
->id));
}
}
return
;
}
sub
_order_station_lines {
my
(
$self
,
$nodes
) =
@_
;
return
unless
scalar
(
keys
%$nodes
);
foreach
my
$node
(
keys
%$nodes
) {
my
$_lines_h
= {};
foreach
(@{
$nodes
->{
$node
}->{line}}) {
$_lines_h
->{
$_
->id} =
$_
if
defined
$_
->name;
}
my
$_lines_a
= [];
foreach
(
sort
keys
%$_lines_h
) {
push
@$_lines_a
,
$_lines_h
->{
$_
};
}
$nodes
->{
$node
}->line(
$_lines_a
);
}
}
sub
_is_visited {
my
(
$id
,
$list
) =
@_
;
foreach
(
@$list
) {
return
1
if
is_same(
$_
,
$id
);
}
return
0;
}
Hide Show 108 lines of Pod
1;