+sub yppedia_graph_spr () {
+ my $base= Graph::Undirected->new();
+ foreach my $na (sort keys %winode2island) {
+ my $ia= $winode2island{$na};
+ foreach my $nb (sort keys %winode2island) {
+ my $ib= $winode2island{$nb};
+ $base->add_weighted_edge($ia,$ib, widist($na,$nb));
+ }
+ }
+ $wispr= shortest_path_reduction('wi',$base);
+}
+
+sub yppedia_ocean_fetch_start ($) {
+ my ($chart) = @_;
+ my @args= ();
+ push @args, '--chart' if $chart;
+ push @args, $ocean;
+ open OCEAN, '-|', "./yppedia-ocean-scraper", @args or die $!;
+}
+sub yppedia_ocean_fetch_done () {
+ $?=0; $!=0; close OCEAN; $? and die $?; $! and die $!;
+}
+
+sub yppedia_ocean_fetch_chart () {
+ if ($stdin_chart) {
+ open OCEAN, "<& STDIN" or die $!;
+ yppedia_chart_parse();
+ } else {
+ yppedia_ocean_fetch_start(1);
+ yppedia_chart_parse();
+ yppedia_ocean_fetch_done();
+ }
+}
+
+sub yppedia_ocean_fetch_text () {
+ yppedia_ocean_fetch_start(0);
+ my $arch;
+ while (<OCEAN>) {
+ chomp;
+ if (m/^ocean /) {
+ $' eq $ocean or die;
+ } elsif (m/^ /) {
+ die unless defined $arch;
+ $wtisland2arch{$'}= $arch;
+ } elsif (m/^ /) {
+ $arch= $';
+ } else {
+ die;
+ }
+ }
+ yppedia_ocean_fetch_done();
+}
+
+sub compare_distances () {
+ foreach my $ia (sort keys %dbisland2arch) {
+ my $na= $wiisland2node{$ia};
+ next unless defined $na;
+ foreach my $ib (sort keys %dbisland2arch) {
+ next unless $ia le $ib; # do every pair only once
+ my $dbdist= $dbspr->get_edge_weight($ia,$ib);
+ my $widist= $wispr->get_edge_weight($ia,$ib);
+ next unless defined $dbdist || defined $widist;
+
+ if (!defined $widist) {
+ warning(sprintf "route delete %2d for %s .. %s",
+ $dbdist, $ia,$ib);
+ } elsif (!defined $dbdist) {
+ change(sprintf "route new %2d for %s .. %s",
+ $widist, $ia,$ib);
+ } elsif ($dbdist != $widist) {
+ change(sprintf "route change %2d to %2d for %s .. %s",
+ $dbdist, $widist, $ia,$ib);
+ }
+ }
+ }
+}
+
+#========== database handling ==========
+
+sub database_fetch_ocean () {
+ my ($row,$sth);
+ $sth= $dbh->prepare('SELECT islandname, archipelago FROM islands');
+ $sth->execute();
+ undef %dbisland2arch;
+ $dbdists= Graph::Undirected->new();
+ while ($row= $sth->fetchrow_hashref) {
+ print DEBUG "database-island $row->{'islandname'}".
+ " $row->{'archipelago'}\n";
+ $dbisland2arch{$row->{'islandname'}}= $row->{'archipelago'};
+ }
+ $sth= $dbh->prepare('SELECT dist, a.islandname a, b.islandname b
+ FROM dists
+ JOIN islands AS a ON dists.aiid==a.islandid
+ JOIN islands AS b ON dists.biid==b.islandid');
+ $sth->execute();
+ while ($row= $sth->fetchrow_hashref) {
+ $dbdists->add_weighted_edge($row->{'a'}, $row->{'b'}, $row->{'dist'});
+ }
+}
+
+sub database_graph_spr () {
+ $dbspr= shortest_path_reduction('db',$dbdists);
+}
+
+sub database_do_updates () {
+ my $addisland= $dbh->prepare(<<'END')
+ INSERT OR IGNORE INTO islands (islandname, archipelago) VALUES (?, ?);
+END
+ ;
+ foreach my $island (sort keys %wiisland2node) {
+ my $wiarch= wiisland2arch($island);
+ $addisland->execute($island, $wiarch);
+ }
+
+ db_doall(<<END)
+ DELETE FROM dists;
+ DELETE FROM routes;
+END
+ ;
+ my $adddist= $dbh->prepare(<<'END')
+ INSERT INTO dists VALUES
+ ((SELECT islandid FROM islands WHERE islandname == ?),
+ (SELECT islandid FROM islands WHERE islandname == ?),
+ ?);
+END
+ ;
+ my $addroute= $dbh->prepare(<<'END')
+ INSERT INTO routes VALUES
+ ((SELECT islandid FROM islands WHERE islandname == ?),
+ (SELECT islandid FROM islands WHERE islandname == ?),
+ ?);
+END
+ ;
+ foreach my $ia (sort keys %wiisland2node) {
+ my $na= $wiisland2node{$ia};
+ foreach my $ib (sort keys %wiisland2node) {
+ my $nb= $wiisland2node{$ib};
+ my $apdist= $ia eq $ib ? 0 : widist($na,$nb);
+ die "$ia $ib" unless defined $apdist;
+ my $sprdist= $wispr->get_edge_weight($ia,$ib);
+ die "$ia $ib $apdist $sprdist" if
+ defined($sprdist) && $sprdist != $apdist;
+
+ $adddist->execute($ia,$ib,$apdist);
+ $addroute->execute($ia,$ib,$sprdist) if defined $sprdist;
+ }
+ }
+
+ # select ia.islandname, ib.islandname, d.dist from dists as d, islands as ia on d.aiid = ia.islandid, islands as ib on d.biid = ib.islandid order by ia.islandname, ib.islandname;
+
+}
+
+#========== update _ocean-*.txt ==========
+
+our $localtopo_path;
+
+sub localtopo_rewrite () {
+ $localtopo_path= '_ocean-'.(lc $ocean).'.txt';
+ my $fh= new IO::File "$localtopo_path.tmp", 'w';
+ print $fh "# autogenerated - do not edit\n" or die $!;
+ print $fh "ocean $ocean\n" or die $!;
+ my %arches;
+ foreach my $isle (sort keys %wtisland2arch) {
+ my $arch= $wtisland2arch{$isle};
+ push @{ $arches{$arch} }, $isle;
+ }
+ foreach my $arch (sort keys %arches) {
+ print $fh " $arch\n" or die $!;
+ foreach my $isle (@{ $arches{$arch} }) {
+ print $fh " $isle\n" or die $!;
+ }
+ }
+ print $fh "\n" or die $!;
+ close $fh or die $!;
+}
+
+sub localtopo_commit () {
+ rename "$localtopo_path.tmp", $localtopo_path or die $!;
+}
+
+#========== main program ==========
+
+parse_info_serverside();
+
+progress("fetching yppedia chart"); yppedia_ocean_fetch_chart();
+progress("adding shortcuts"); yppedia_graphs_add_shortcuts();
+progress("pruning boring vertices"); yppedia_graphs_prune_boring();
+progress("checking yppedia graphs"); yppedia_graphs_check();
+progress("setting archs from source-info"); yppedia_archs_sourceinfo();
+progress("computing shortest paths"); yppedia_graph_shortest_paths();
+progress("setting archs from labels"); yppedia_archs_chart_labels();
+progress("setting archs from nearby"); yppedia_archs_fillbynearest();
+progress("computing yppedia spr"); yppedia_graph_spr();
+
+if (!$stdin_chart) {
+ progress("fetching yppedia ocean text"); yppedia_ocean_fetch_text();
+}
+
+db_setocean($ocean);