#!/usr/bin/perl # # map-collection.pl # Generate some HTML pages from a map-collection.txt database file. # # usage: map-collection.pl # writes map-collection-details.include and map-collection-categories.include # # # Copyright 1999 by Darren Stuart Embry . # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # The GNU General Public License can be downloaded from # # incase you don't already have a copy. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # # # # 1999-08-08 dse+mapspage@webonastick.com: Initial working version. # # I may make minor changes to this script that won't be documented here, # but I will try to document major ones. I may decide to write comments # someday. :) # # # ############################################################################### package MapCollection; require Exporter; use HTML::Parser; @ISA = qw(Exporter HTML::Parser); @EXPORT = qw(); @EXPORT_OK = qw(); BEGIN { %shortcuts = ( 'idx' => 'index', 'tit' => 'title', 'fmt' => 'format', 'clr' => 'color', 'dat' => 'date', 'crt' => 'cartographer', 'pub' => 'publisher', 'ar' => 'area', 'a' => 'area', 'in' => 'inset', 'i' => 'inset', 'co' => 'comment', 'd' => 'detail', ); %entry_primary_attr = ( 'index' => 1, 'title' => 1, 'format' => 1, 'color' => 1, 'url' => 1, 'url-note' => 1, 'date' => 1, 'cartographer' => 3, 'original_cartographer' => 3, 'publisher' => 3, 'area' => 2, 'inset' => 2, 'comment' => 2, 'copies' => 1, 'isbn' => 1, 'coverage' => 1, ); %entry_secondary_attr = ( 'detail' => 1, 'scale' => 1, 'addr' => 1, 'phone' => 1, 'fax' => 1, 'url' => 1, 'email' => 1, ); %default_secondary = ( 'comment' => 'text', 'area' => 'title', 'inset' => 'title', 'cartographer' => 'name', 'publisher' => 'name', 'original_cartographer' => 'name', ); %entry_primary_multi = ( 'index' => ',', ); %html_passthru = qw(a 1 b 1 i 1); } sub new { my $class = shift; my $self = new HTML::Parser; bless $self,$class; $self->init(); return $self; } sub init { my $self = shift; $self->{'entries'} = []; } sub declaration { my $self = shift; my $decl = shift; } sub start { my $self = shift; my ($tag,$attr,$attrseq,$origtext) = @_; warn "start tag: $tag\n" if $VERBOSE; if (exists($shortcuts{$tag}) and ($self->{'reading_primary_attr'} ne 'comment') ) { $tag = $shortcuts{$tag}; } if ($tag eq 'entry') { warn " (new entry)\n" if $VERBOSE; my $new_entry = {}; push(@{$self->{'entries'}},$new_entry); $self->{'primary_entry'} = $new_entry; $self->{'reading_primary_attr'} = 'title'; $self->{'reading_secondary_attr'} = undef; if (exists $attr->{'id'}) { $new_entry->{'id'} = $attr->{'id'}; } } elsif ($entry_secondary_attr{$tag} and $self->{'reading_secondary_attr'} ne '') { if ($self->{'reading_primary_attr'} ne '') { warn " (secondary)\n" if $VERBOSE; $self->{'reading_secondary_attr'} = $tag; $self->{'secondary_entry'}->{$tag} = ''; } } elsif ($entry_primary_attr{$tag}) { warn " (primary)\n" if $VERBOSE; $self->{'reading_primary_attr'} = $tag; $self->{'reading_secondary_attr'} = undef; if ($entry_primary_attr{$tag} == 1) { if ($entry_primary_multi{$tag}) { if (exists $self->{'primary_entry'}->{$tag}) { $self->{'primary_entry'}->{$tag} .= $entry_primary_multi{$tag}; } else { $self->{'primary_entry'}->{$tag} = ''; } } else { $self->{'primary_entry'}->{$tag} = ''; } } elsif ($entry_primary_attr{$tag} == 2) { my $reading_secondary_entry = {}; push(@{$self->{'primary_entry'}->{$tag}}, $reading_secondary_entry); $self->{'secondary_entry'} = $reading_secondary_entry; $self->{'reading_secondary_attr'} = $default_secondary{$tag}; $self->{'secondary_entry'}->{$default_secondary{$tag}} = ''; } elsif ($entry_primary_attr{$tag} == 3) { my $reading_secondary_entry = {}; $self->{'primary_entry'}->{$tag} = $reading_secondary_entry; $self->{'secondary_entry'} = $reading_secondary_entry; $self->{'reading_secondary_attr'} = $default_secondary{$tag}; $self->{'secondary_entry'}->{$default_secondary{$tag}} = ''; } } elsif ($html_passthru{$tag}) { $self->append_($origtext); } } sub end { my $self = shift; my ($tag,$origtext) = @_; if ($tag eq 'entry') { $self->{'primary_entry'} = undef; } elsif ($entry_primary_attr{$tag}) { $self->{'reading_primary_attr'} = undef; $self->{'reading_secondary_attr'} = undef; } elsif ($entry_secondary_attr{$tag}) { $self->{'reading_secondary_attr'} = undef; } elsif ($html_passthru{$tag}) { $self->append_($origtext); } } sub append_ { my $self = shift; my $text = shift; my $primary = $self->{'reading_primary_attr'}; my $secondary = $self->{'reading_secondary_attr'}; if ($primary ne '') { if ($secondary ne '') { warn "appending to $primary/$secondary: [[$text]]\n" if $VERBOSE; $self->{'secondary_entry'}->{$secondary} .= $text; } else { warn "appending to $primary: [[$text]]\n" if $VERBOSE; $self->{'primary_entry'}->{$primary} .= $text; } } } sub text { my $self = shift; my $text = shift; $self->append_($text); } sub comment { my $self = shift; my $comment = shift; } sub collapse_ws { foreach (@_) { s/^\s+//gms; s/\s+$//gms; s/\s+/ /gms; } } sub eof { my $self = shift; $self->SUPER::eof(); foreach my $entry (@{$self->{'entries'}}) { foreach ($entry->{'index'}) { s/\s+//g; s/,,+/,/g; s/^,+//g; s/,+$//g; my @index = split(',',$_); $_ = join(',',map { '{'.$_.'}' } @index); } foreach my $key (sort keys %$entry) { warn "$key:\n" if $VERBOSE; if (ref($entry->{$key}) eq 'ARRAY') { foreach my $entry (@{$entry->{$key}}) { foreach my $key2 (keys %{$entry}) { warn " $key2\n" if $VERBOSE; collapse_ws($entry->{$key2}); } } } elsif (ref($entry->{$key}) eq 'HASH') { foreach my $key2 (keys %{$entry->{$key}}) { warn " $key2\n" if $VERBOSE; collapse_ws($entry->{$key}->{$key2}); } } else { collapse_ws($entry->{$key}); } } } } ############################################################################### package main; import MapCollection; use HTML::Entities; my $parser = new MapCollection; while (<>) { $parser->parse($_); } $parser->eof(); sub abbreviate { if (defined wantarray) { my @foo = @_; abbreviate(@foo); return @foo if wantarray; return $foo[0]; } foreach (@_) { print STDERR "abbr $_ => " if $VERBOSE; tr/A-Z/a-z/; s/[^a-z0-9\.\s]//gi; print STDERR "$_\n" if $VERBOSE; } } sub hyperlink { my $title = shift; my $url = shift; return "".$title."" if $url ne ''; return $title; } foreach my $entry (@{$parser->{'entries'}}) { foreach my $key ('publisher','cartographer') { my $who = $entry->{$key}; if (defined $who) { my $url = $who->{'url'}; $who = $who->{'name'}; abbreviate($who); if ($url{$who} eq '' and $url ne '') { warn "$who => $url\n" if $VERBOSER; $url{$who} = $url; } } } } sub pub_and_cart { my $entry = shift; my $publisher = $entry->{'publisher'}; my $cartographer = $entry->{'cartographer'}; $publisher = $cartographer if not defined $publisher or $publisher->{'name'} eq ''; $cartographer = $publisher if not defined $cartographer or $cartographer->{'name'} eq ''; $publisher = $publisher->{'name'} if defined $publisher; $cartographer = $cartographer->{'name'} if defined $cartographer; my $pub = abbreviate($publisher); my $cart = abbreviate($cartographer); my $publisher_url = $url{$pub}; my $cartographer_url = $url{$cart}; return ($publisher,$cartographer, $pub,$cart, $publisher_url,$cartographer_url); } sub areas { my $entry = shift; my %opts = @_; if ($opts{'detail'}) { return map { $_->{'title'} . ( ($_->{'detail'} ne '' ) ? (' (' . $_->{'detail'} . ')') : '') } @{$entry->{'area'}}; } else { return map { $_->{'title'} } @{$entry->{'area'}}; } } sub insets { my $entry = shift; my %opts = @_; if ($opts{'detail'}) { return map { $_->{'title'} . ( ($_->{'detail'} ne '' ) ? (' (' . $_->{'detail'} . ')') : '') } @{$entry->{'inset'}}; } else { return map { $_->{'title'} } @{$entry->{'inset'}}; } } sub comments { my $entry = shift; return map { $_->{'text'} } @{$entry->{'comment'}}; } sub assign_entry_ids { my $entry_id = 0; foreach my $entry (@{$parser->{'entries'}}) { if (not exists $entry->{'id'}) { ++$entry_id; $entry->{'id'} = 'e' . $entry_id; } } } sub write_detailed { my $fh = shift; $fh->print("
\n"); foreach my $entry (@{$parser->{'entries'}}) { my $entry_id = $entry->{'id'}; my $title = $entry->{'title'}; my $map_url = $entry->{'url'}; my $map_url_note = $entry->{'url-note'}; my ($publisher,$cartographer, $pub,$cart, $publisher_url,$cartographer_url) = pub_and_cart($entry); my $date = $entry->{'date'}; my @areas = areas($entry,'detail' => 1); my @insets = insets($entry,'detail' => 1); my @comments = comments($entry); my $index = $entry->{'index'}; # foreach (@areas,@insets,$index, # $date,$title,$publisher,$cartographer) { # encode_entities($_); # } my $areas = join('; ',@areas); my $insets = join('; ',@insets); my $comments = join('; ',@comments); foreach ($areas,$insets,$comments,$index, $date,$title,$publisher,$cartographer) { # $_ = " " if $_ eq ''; } $title = '(untitled)' if $title eq ''; $publisher = hyperlink($publisher,$publisher_url) if $publisher_url ne ''; $cartographer = hyperlink($cartographer,$cartographer_url) if $cartographer_url ne ''; my $pubcart; if ($pub eq $cart) { $pubcart = "Publisher: $publisher"; } else { $pubcart = "Publisher: $publisher"; $pubcart .= "
Cartographer: $cartographer"; } $fh->print("
$title ($date)", "
\n", "

$pubcart\n", "

Coverage Areas: $areas\n"); $fh->print("
Insets: $insets\n") if $insets ne ''; $fh->print("

Comments: $comments
\n") if $comments ne ''; $fh->printf("

How to acquire this map: details ". "are available on a separate page. %s", encode_entities($map_url), $map_url_note) if $map_url ne ''; $fh->print("

\n"); } $fh->print("
\n"); } sub indexed { my $entry = shift; my $index_type = shift; return $entry->{'index'} =~ /\{\Q$index_type\E\b[^\}]*\}/; } sub categorize_entries { foreach my $entry (@{$parser->{'entries'}}) { my $index = $entry->{'index'}; my $Louisville = (indexed($entry,'city:us/ky/louisville') or indexed($entry,'county:us/ky/jefferson')); my $Old = indexed($entry,'old'); my $NE_Ohio = (indexed($entry,'city:us/oh/cleveland') or indexed($entry,'city:us/oh/akron') or indexed($entry,'city:us/oh/canton') or indexed($entry,'county:us/oh/cuyahoga') or indexed($entry,'county:us/oh/stark') or indexed($entry,'county:us/oh/wayne') or indexed($entry,'county:us/oh/lake') or indexed($entry,'county:us/oh/geauga') or indexed($entry,'county:us/oh/lorain') or indexed($entry,'county:us/oh/medina') or indexed($entry,'region:us/oh/northeastern') ); if (indexed($entry,'transit')) { push(@entries_transit,$entry); } elsif (indexed($entry,'rec') or indexed($entry,'bike')) { push(@entries_rec,$entry); } elsif (indexed($entry,'usgs') and (indexed($entry,'topo') or indexed($entry,'topographic'))) { push(@entries_topo,$entry); } elsif ($Old) { if ($Louisville) { push(@entries_old_louisville,$entry); } elsif ($NE_Ohio) { push(@entries_old_ne_ohio,$entry); } else { push(@entries_old,$entry); } } elsif ($Louisville) { push(@entries_city_louisville,$entry); } elsif ($NE_Ohio) { push(@entries_ne_ohio,$entry); } elsif (indexed($entry,'city:us/ky') or indexed($entry,'county:us/ky')) { push(@entries_city_kentucky,$entry); } elsif (indexed($entry,'city') or indexed($entry,'county') or indexed($entry,'parish')) { push(@entries_city,$entry); } elsif ((indexed($entry,'state') or indexed($entry,'province')) and indexed($entry,'official')) { push(@entries_state_official,$entry); } elsif ((indexed($entry,'state') or indexed($entry,'province')) and indexed($entry,'tourism')) { push(@entries_state_tourism,$entry); } elsif (indexed($entry,'state') or indexed($entry,'province')) { push(@entries_state,$entry); } elsif (indexed($entry,'atlas')) { push(@entries_atlas,$entry); } else { push(@entries_other,$entry); } } } sub write_entry_table_row { my $fh = shift; my $entry = shift; my $id = $entry->{'id'}; my $title = $entry->{'title'}; my $date = $entry->{'date'}; my ($publisher,$cartographer, $pub,$cart, $publisher_url,$cartographer_url) = pub_and_cart($entry); my @areas = areas($entry); my $areas = join('; ',@areas); # foreach ($title,$date,$publisher,$cartographer,$areas) { # encode_entities($_); # } $title = hyperlink($title,"collection-details.html#$id"); $publisher = hyperlink($publisher,$publisher_url); $cartographer = hyperlink($cartographer,$cartographer_url); $fh->print("\n"); $fh->print("$title"); $fh->print("$date"); $fh->print("$publisher$cartographer") if $pub ne $cart; $fh->print("$publisher") if $pub eq $cart; $fh->print("$areas"); $fh->print("\n"); } sub write_entry_category { my $fh = shift; $fh->print(""); foreach my $h ('Title (Link to Details)', 'Date', 'Publisher', 'Cartographer', 'Coverage Area(s)') { $fh->printf("",$h); } $fh->print("\n"); foreach my $entry (@_) { write_entry_table_row($fh,$entry); } $fh->print("
%s
\n"); } sub H2 { my $fh = shift; my $title = shift; my $name = shift; $fh->print("

"); $fh->print("") if $name ne ''; $fh->print($title); $fh->print("") if $name ne ''; $fh->print("

\n"); } sub write_entry_categories { my $fh = shift; my @categories = ( [\@entries_city_louisville, 'Street Maps of Louisville, KY', 'city-louisville'], [\@entries_old_louisville, 'Old Street Maps of Louisville, KY', 'old-louisville'], [\@entries_city_kentucky, 'Street Maps of Cities and Counties elsewhere in Kentucky', 'city-kentucky'], [\@entries_ne_ohio, 'Maps of Northeastern Ohio', 'city-ne-ohio'], [\@entries_old_ne_ohio, 'Old Maps of Northeastern Ohio', 'old-ne-ohio'], [\@entries_city, 'Street Maps of Cities and Counties outside Kentucky', 'city'], [\@entries_state_official, 'State Official (D.o.T.) Maps', 'st-off'], [\@entries_state_tourism, 'State Tourism Highway Maps', 'st-tour'], [\@entries_state, 'Commercial State Highway Maps', 'st-com'], [\@entries_rec, 'Recreational Maps', 'rec'], [\@entries_topo, 'USGS Topographic Maps', 'topo'], [\@entries_transit, 'Public Transit System Maps', 'transit'], [\@entries_atlas, 'Road Atlases', 'atlas'], [\@entries_old, 'Other Old Maps', 'old'], [\@entries_other, 'Miscellaneous Maps', 'misc'], ); H2($fh,"Table Of Contents",'toc'); $fh->print("\n"); foreach my $category (@categories) { my ($listref,$title,$name) = @$category; H2($fh,$title,$name); write_entry_category($fh,@$listref); } } sub main { assign_entry_ids; categorize_entries; use IO::File; use Env qw(HOME); { my $file_details = "./collection-details.include"; my $fh = new IO::File ('>'.$file_details); die "Could not write $file_details: $!\n" if not defined $fh; write_detailed($fh); $fh->close(); } { my $file_categories = "./collection-categories.include"; my $fh = new IO::File ('>'.$file_categories); die "Could not write $file_categories: $!\n" if not defined $fh; write_entry_categories($fh); $fh->close(); } } umask(022); main;