#!/usr/bin/perl
# Copyright       : http://www.fsf.org/copyleft/gpl.html
# Author          : Dan Jacobson -- http://jidanni.org/geo/taipower/
# Created On      : Thu Mar 16 17:37:29 2006
# Last Modified On: Sat Mar  7 01:50:23 2020
# Update Count    : 488

=encoding utf8

=head1 DESCRIPTION

轉換台灣電力公司設備座標 < = > 公尺座標。
Taipowergrid converts Taiwan Power Company coordinates to and from X
and Y meters coordinates.

=head1 USAGE
$ taipowergrid
輸入 with standard input of
   #A comment
   329999 2449999
   W9999 HE9999
   354999 2663999 119
輸出 gives output:
   ##A comment
   #1 329999 2449999
   W9999 HE9999
   #2 W9999 HE9999
   329999 2449999
   #3 354999 2663999 119
   X9999 HE9999
加註 119 為澎湖,無則台、金、馬。
The 119 specifies the Penghu meridian,
else Taiwan, Jinmen, or Mazu is assumed.
W9999HE9999 無空格輸入亦可。 No-space input also OK.
=cut

use strict;
use warnings FATAL       => 'all';
use constant II          => "輸入錯誤 invalid input";
use constant D_EW        => 80000;                        #Dimension east-west
use constant D_NS        => 50000;                        #Dimension north-south
use constant TAIWAN_LEFT => 90000;
use constant TAIWAN_TOP  => 2800000;
use constant PENGHU_LEFT => 275000;
use constant PENGHU_BOTTOM => 2564000;
use constant JINMEN_LEFT   => 10000;                      #552700;
use constant JINMEN_BOTTOM => 2675800;
use constant MAZU_LEFT     => 10000;                      #790400
use constant MAZU_BOTTOM   => 2894000;
my %baselines = (
    S => [ MAZU_LEFT,   MAZU_BOTTOM ],
    Y => [ PENGHU_LEFT, PENGHU_BOTTOM ],
    X => [ PENGHU_LEFT, PENGHU_BOTTOM + D_NS ],
    Z => [ JINMEN_LEFT, JINMEN_BOTTOM ]
);
use constant TAIWAN_MAP => '
_ABC
_DEF
_GH_
JKL_
MNO_
PQR_
_TU_
_VW';
my $taiwan_bottom = ( undef, TAIWAN_TOP + D_NS );

for ( split /\n/, TAIWAN_MAP ) {
    my $left_edge = 10000;
    $taiwan_bottom -= D_NS;
    for ( split // ) {
        $left_edge += D_EW;
        $baselines{$_} = [ $left_edge, $taiwan_bottom ] if /[[:upper:]]/;
    }
}

my $conversion;
while (<>) {
    if (/^(#|$)/) { print "#$_"; next }    #comments
    print "#", ++$conversion, " ", $_;
    chomp;
    if   (/^\d/) { print join( " ", xy_to_electric($_) ), "\n" }
    else         { print join( " ", electric_to_xy($_) ), "\n" }
}

sub electric_to_xy {
    die II
      unless my ( $area_letter, @electric ) =
      /^([A-HJ-Z])(\d\d)(\d\d)\s*([A-H])([A-E])(\d)(\d)(?:(\d)(\d))?$/;
    $electric[0] = 50 + ( $electric[0] + 50 ) % 100 if $area_letter eq 'Z';
    my @xy = electric80000x50000_to_xy(@electric);
    $xy[$_] += $baselines{$area_letter}[$_] for 0 .. 1;
    push @xy, 119 if $area_letter =~ /[XY]/;
    return @xy;
}

sub electric80000x50000_to_xy {
    my @xy = ( 800 * shift, 500 * shift );
    $_ += ( ord(shift) - ord 'A' ) * 100 for @xy;
    $_ += 10 * shift for @xy;
    $_ += shift // 0 for @xy;
    return @xy;
}

sub xy_to_electric {
    die II unless my @xy = /^(\d+)\s+(\d+)(\s+\d+)?$/;
    my $area_letter = area_letter_of(@xy);
    $xy[$_] -= $baselines{$area_letter}[$_] for 0 .. 1;
    $xy[0] %= D_EW if $area_letter eq 'Z';
    return $area_letter . xy_to_electric80000x50000(@xy);
}

sub area_letter_of {
    if ( defined $_[2] ) {
        die
"澎湖加註 119. 其餘自動偵測。 Append 119 for Penghu, other areas automatically detected"
          unless $_[2] == 119;
        die II if $_[0] < PENGHU_LEFT;
        die II if $_[0] >= PENGHU_LEFT + D_EW;
        die II if $_[1] < PENGHU_BOTTOM;
        my $i;
        for ( 'Y', 'X' ) {
            return $_ if $_[1] < PENGHU_BOTTOM + D_NS * ++$i;
        }
        die II;
    }
    if ( $_[1] >= MAZU_BOTTOM ) {
        die II unless $_[1] < MAZU_BOTTOM + D_NS;
        die II unless $_[0] >= MAZU_LEFT;
        die II unless $_[0] < MAZU_LEFT + D_EW;
        return 'S';
    }
    if ( $_[0] < JINMEN_LEFT + D_EW * 3 / 2 ) {
        die II unless $_[0] >= JINMEN_LEFT;
        die II unless $_[1] >= JINMEN_BOTTOM;
        die II unless $_[1] < JINMEN_BOTTOM + D_NS;
        return 'Z';
    }
    die II if $_[1] >= TAIWAN_TOP;
    die II if $_[1] < $taiwan_bottom;
    my $char_position =
      int( ( TAIWAN_TOP - $_[1] - 1 ) / D_NS ) * 5 +
      int( ( $_[0] - TAIWAN_LEFT ) / D_EW ) + 1;
    die II if $char_position > length TAIWAN_MAP;
    die II if ( my $char = substr TAIWAN_MAP, $char_position, 1 ) !~ /[A-W]/;
    return $char;
}

sub xy_to_electric80000x50000 {
    return sprintf "%02d%02d %s%s%d%d%d%d",
      map( int, $_[0] / 800, $_[1] / 500 ),
      chr( ord('A') + int( $_[0] % 800 / 100 ) ),
      chr( ord('A') + int( $_[1] % 500 / 100 ) ),
      map( int, $_[0] % 100 / 10, $_[1] % 100 / 10 ), $_[0] % 10, $_[1] % 10;
}