You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki-map/wiki-map.pl

396 lines
11 KiB

use v5.36;
1 year ago
use HexGrid;
use HexGrid::Pin;
use MWTemplate;
use MediaWiki::API;
use Getopt::Long;
1 year ago
use Carp;
use Data::Dumper;
# $Data::Dumper::Indent = 1;
1 year ago
use feature "signatures";
no warnings "experimental::signatures";
1 year ago
# This regex is a whitespace forgiving version of /^(-?\d+),(-?\d+)/, an int pair
my $coords_regex = qr/^\s*(-?\s*\d+)\s*,\s*(-?\s*\d+)\s*$/;
1 year ago
my $api_url;
1 year ago
my $region_template_name = "MapRegion";
1 year ago
my $subregion_template_name = "MapSubregion";
my $location_template_name = "MapLocation";
1 year ago
my $site_template_name = "MapSite";
my $border_width = 1;
my $border_colour = 'black';
my $show_coords = 0;
1 year ago
my $embed_images = 1;
1 year ago
my $html_document = 1;
my $outfile = '-';
my $regiondir;
GetOptions
(
'api-url=s' => \$api_url,
'region-template-name=s' => \$region_template_name,
'subregion-template-name=s' => \$subregion_template_name,
'location-template-name=s' => \$location_template_name,
'site-template-name=s' => \$site_template_name,
'border-width|bw=f' => \$border_width,
'border-colour|border-color|bc=s' => \$border_colour,
'show-coords|coords!' => \$show_coords,
1 year ago
'embed-images!' => \$embed_images,
'html-document!' => \$html_document,
'outfile=s' => \$outfile,
'regiondir=s' => \$regiondir
);
$api_url // croak "Base API URL is required! Use --api-url to set";
1 year ago
my $grid = HexGrid->new(embed_images => $embed_images, defaults => {
1 year ago
style => { 'stroke-width' => $border_width, stroke => $border_colour },
1 year ago
show_coords => $show_coords});
1 year ago
# my %region_grids;
my %regions_by_subregion;
my %region_grid_listings;
my %images_for_region_grids;
1 year ago
my $mw = MediaWiki::API->new();
$mw->{config}->{api_url} = $api_url;
say STDERR "Getting Region pages";
my $region_query_results = $mw->api
( {
1 year ago
action => 'query',
generator => 'categorymembers',
gcmtitle => 'Category:Regions',
gcmlimit => 'max',
prop => 'info|revisions',
rvprop => 'content',
inprop => 'url',
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
my (@tile_pages, %background_pages);
foreach my $page (values %{$region_query_results->{query}{pages}})
{
next if $page->{title} =~ /^Category:/;
my $region = $grid->make_region($page->{title});
my $parsed_template = MWTemplate::Parse($page->{revisions}[0]{'*'}, $region_template_name);
next unless $parsed_template;
say STDERR "Processing region: $page->{title}";
$region->{defaults}{colour} = $parsed_template->{named_params}{colour};
if($regiondir)
{
1 year ago
# $region_grid_listings{$region->{name}} = [$region->{name}];
$regions_by_subregion{$region->{name}} = $region->{name};
}
push @tile_pages, "$page->{title}/Tiles";
push @{$background_pages{"File:$parsed_template->{named_params}{background}"}}, $region;
}
1 year ago
say STDERR "Getting Subregion pages";
my $subregion_query_results = $mw->api
( {
action => 'query',
generator => 'categorymembers',
gcmtitle => 'Category:Subregions',
gcmlimit => 'max',
prop => 'info|revisions',
rvprop => 'content',
inprop => 'url',
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
foreach my $page (values %{$subregion_query_results->{query}{pages}})
{
next if $page->{title} =~ /^Category:/;
1 year ago
my $parsed_template = MWTemplate::Parse($page->{revisions}[0]{'*'}, $subregion_template_name);
next unless $parsed_template;
say STDERR "Processing subregion: $page->{title}";
1 year ago
my $subregion = $grid->make_region($page->{title});
$subregion->{defaults}{colour} = $parsed_template->{named_params}{colour};
if($regiondir)
{
my $region_name = $parsed_template->{positional_params}[0];
1 year ago
$regions_by_subregion{$subregion->{name}} = $region_name;
}
1 year ago
push @tile_pages, "$page->{title}/Tiles";
push @{$background_pages{"File:$parsed_template->{named_params}{background}"}}, $subregion;
}
my @location_continuations;
say STDERR "Getting Location pages";
my $location_query_results = $mw->api
( {
action => 'query',
generator => 'categorymembers',
gcmtitle => 'Category:Locations',
gcmlimit => 'max',
prop => 'info|revisions',
rvprop => 'content',
inprop => 'url',
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
foreach my $page (values %{$location_query_results->{query}{pages}})
{
next if $page->{title} =~ /^Category:/;
my $parsed_template = MWTemplate::Parse($page->{revisions}[0]{'*'}, $location_template_name);
next unless $parsed_template;
1 year ago
say STDERR "Processing location: $page->{title}";
1 year ago
my $location = $grid->make_region($page->{title});
$location->{defaults}{colour} = $parsed_template->{named_params}{colour};
push @{$background_pages{"File:$parsed_template->{named_params}{background}"}}, $location;
1 year ago
my $region_name = $parsed_template->{positional_params}[1];
$regions_by_subregion{$location->{name}} = $region_name;
push @location_continuations, sub
{
return unless $parsed_template->{positional_params}[0] =~ $coords_regex;
my ($nw, $sw) = ($1, $2);
$location->make_tile_at($nw, $sw);
if($regiondir)
{
1 year ago
# $region_grids{$region_name}->add_region($location);
# $region_grids{$location->{name}} = HexGrid->new
# (
# defaults =>
# {
# style => { 'stroke-width' => $border_width, stroke => $border_colour },
# show_coords => $show_coords
# },
# height => 300,
# width => 300
# );
# my $location_with_context = $region_grids{$location->{name}}->make_region($location->{name});
# $location_with_context->add_tile($grid->get_tile_at($nw, $sw));
# foreach my $coords (split /;/, $parsed_template->{named_params}{context_tiles})
# {
# do { carp "Skipping bad spec: $coords"; next; } unless $coords =~ $coords_regex;
# $location_with_context->add_tile($grid->get_tile_at($1, $2));
# }
}
};
}
1 year ago
HexGrid::Image::DEBUG();
say STDERR "Getting Background image pages";
my $background_query_results = $mw->api({ action => 'query',
prop => 'imageinfo',
titles => join('|', keys %background_pages),
iiprop => 'url'
}) || carp $mw->{error}->{code} . ': ' . $mw->{error}->{details};
1 year ago
1 year ago
# say STDERR Dumper(\%background_pages);
foreach my $page (values %{$background_query_results->{query}{pages}})
{
if($page->{imageinfo})
{
say STDERR "Processing image: $page->{title}";
1 year ago
$grid->add_image(HexGrid::to_id($page->{title}), $page->{imageinfo}[0]{url});
1 year ago
foreach my $subregion (@{$background_pages{$page->{title}}})
{
1 year ago
$subregion->{defaults}{image} = HexGrid::to_id($page->{title});
1 year ago
if($regiondir)
{
1 year ago
my $region_name = $regions_by_subregion{$subregion->{name}};
# say STDERR $page->{title} unless $region_name;
# say STDERR "$region_name";
push @{$images_for_region_grids{$region_name}},
1 year ago
{
1 year ago
name => HexGrid::to_id($page->{title}),
source => $page->{imageinfo}[0]{url}
};
1 year ago
}
}
}
}
1 year ago
# say STDERR Dumper($grid->{images});
# say STDERR Dumper(\%images_for_region_grids);
say STDERR "Getting Tile pages";
my $tile_query_results = $mw->api
( {
action => 'query',
titles => join('|', @tile_pages),
prop => 'revisions',
rvprop => 'content',
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
1 year ago
foreach my $page (values %{$tile_query_results->{query}{pages}})
1 year ago
{
my $content = $page->{revisions}[0]{'*'};
my ($region_name) = $page->{title} =~ /(.*)\/Tiles/;
say STDERR "Processing tiles for: $region_name";
my $region = $grid->{regions}{$region_name};
foreach my $coords (split /;/, $content)
{
do { carp "Skipping bad spec: $coords"; next; } unless $coords =~ $coords_regex;
$region->make_tile_at($1,$2);
}
1 year ago
}
say STDERR "Continuing Location processing";
$_->() for @location_continuations;
1 year ago
say STDERR "Getting Site pages";
my $site_query_results = $mw->api
1 year ago
( {
action => 'query',
generator => 'categorymembers',
prop => 'info|revisions',
gcmtitle => 'Category:Sites',
gcmlimit => 'max',
rvprop => 'content',
inprop => 'url',
1 year ago
} ) || croak $mw->{error}->{code} . ': ' . $mw->{error}->{details};
# say STDERR "Sites found: " . join(" ,", map { $_->{title} } @$site_pages);
foreach my $site_page_ref (values %{$site_query_results->{query}{pages}})
{
next if $site_page_ref->{title} =~ /^Category:/;
1 year ago
my $site_name = $site_page_ref->{title};
say STDERR "Processing Site $site_name";
my $site_url = $site_page_ref->{canonicalurl};
my $site_content = $site_page_ref->{revisions}[0]{'*'};
my $parsed_template = MWTemplate::Parse($site_content, $site_template_name);
next unless $parsed_template;
1 year ago
my ($nw,$sw) = split /,/, $parsed_template->{named_params}{coords};
my $imageinfo_query_results = $mw->api({ action => 'query',
prop => 'imageinfo',
titles => "File:$parsed_template->{named_params}{icon}",
iiprop => 'url'
}) || carp $mw->{error}->{code} . ': ' . $mw->{error}->{details};
my %image_pages = %{$imageinfo_query_results->{query}{pages}};
my $image_url = (values %image_pages)[0]{imageinfo}[0]{url};
1 year ago
$grid->add_image(HexGrid::to_id($parsed_template->{named_params}{icon}), $image_url);
1 year ago
my $pin = HexGrid::Pin->new
(
name => $site_name,
1 year ago
id => HexGrid::to_id($site_name),
icon => HexGrid::to_id($parsed_template->{named_params}{icon}),
1 year ago
link => $site_url,
description => $parsed_template->{named_params}{abstract}
);
$grid->get_tile_at($nw, $sw)->pin($pin);
}
open (my $fh, "> $outfile") or croak "Couldn't open $outfile for writing: $!";
say $fh ($html_document ? wrap_in_html($grid) : $grid->render);
close $fh;
if($regiondir)
{
chdir $regiondir || croak "Couldn't chdir to $regiondir: $!";
my $extension = $html_document ? 'html' : 'svg';
1 year ago
my %region_grid_listings;
while(my ($subregion, $region) = each %regions_by_subregion)
{
1 year ago
push @{$region_grid_listings{$region}}, $subregion;
}
while(my ($region, $subregions) = each %region_grid_listings)
{
my $region_grid = $grid->subgrid_for_regions(@$subregions);
# say STDERR Dumper($images_for_region_grids{$region});
if(exists $images_for_region_grids{$region})
{
foreach my $image (@{$images_for_region_grids{$region}})
{
# say STDERR Dumper($image);
$region_grid->add_image($image->{name}, $image->{source});
}
}
open (my $region_fh, "> $region.$extension")
or croak "Couldn't open $region.extension for writing: $!";
say $region_fh ($html_document ? wrap_in_html($region_grid) : $region_grid->render);
close $region_fh;
}
}
1 year ago
1 year ago
### Subgrid testing
1 year ago
# HexGrid::DEBUG();
# my $subgrid = $grid->subgrid_for_regions("Midhills", "Minev's Forest", "Naurardhon");
# my $subgrid = $grid->subgrid_for_tiles
# (
# { nw => 0, sw => 0 },
# { nw => 1, sw => 0 },
# { nw => 0, sw => 1 },
# # { nw => 1, sw => 1 },
# { nw => -1, sw => 0 },
# { nw => -1, sw => 1 },
# { nw => 1, sw => -1 }
# # { nw => 0, sw => -1 },
# # { nw => -1, sw => -1 }
# );
# my $svg = $subgrid->render;
# open SUBGRID, ">subgrid_test.svg";
# say SUBGRID $svg;
# close SUBGRID;
1 year ago
###
1 year ago
sub wrap_in_html($grid)
{
my $html_builder = "<!DOCTYPE html>";
$html_builder .= "\n<html>\n<body>";
$html_builder .= "\n" . <<EOS;
<script>
function clickPin(pinId, containerId) {
let popup = document.getElementById(pinId + '-popup');
popup.style.visibility = popup.style.visibility == 'visible' ? 'hidden' : 'visible';
}
</script>
EOS
if($show_coords)
{
$html_builder .= <<EOS;
<script>
function toggleCoords(show) {
for (var elem of document.getElementsByClassName('coords')) {
elem.style.visibility = show ? 'visible' : 'hidden';
}
}
</script>
<label for="show-coords-checkbox">Show coordinates</label>
<input type="checkbox" checked id="show-coords-checkbox" onclick="toggleCoords(event.srcElement.checked)" />
EOS
}
1 year ago
$html_builder .= "\n" . $grid->render;
$html_builder .= "\n</body>\n</html>";
return $html_builder;
}