使用 Perl、GD 和 plot-latlong 创建数据的地理标绘

来源:岁月联盟 编辑:zhu 时间:2009-03-05

  本文示例源代码或素材下载

  人员和组织通常需要使用地图表示实现数据可视化。现有的地图工具(例如 Google 地图)提供了优秀的方法(虽然有限)。本文将把简单的 Perl 代码用于使用 Yahoo 的免费地理编码服务进行的地理编码查找。使用已获得的纬度和经度,本文随后将展示如何使用 CAIDA 的 plot-latlong 应用程序以及使用 GD 的注释来制作几乎有无限选项的有用图形。

  在构建混合的 plot-latlong 和 Perl-GD 地图后,我们将修改背景地图轮廓。使用 GD 的填充工具和其他工具,我们将创建具有 “色彩渐变” 版本的美国地图,以表示地理点,以及与州相关的数量数据。

  本文介绍的工具和技术将使您可以使用开源软件和可免费获得的地理编码服务来创建您自己的自定义地理数据图。现有的地图工具 API 可以让您标绘世界各地的地理点,但是您可能需要一种轻松的方法根据特定数据对州、国家或地区使用色彩渐变效果。您还可能需要为数以千计的地理点实时提供配有可自定义指示器的坐标图。GD、Perl 和 plot-latlong 都是快速创建高效地图所需的工具。

  要求

  硬件

  2000 年后制造的所有 PC 应当能够提供足够的能力编译和运行本文中的代码。CAIDA 的 plot-latlong 和 GD 将执行大部分处理,并且这些程序都经过良好设计而且速度很快。

  软件

  假定您选择的运行环境包含最新版本的 Perl,您将需要安装 Geocoding 模块和 GD Perl 模块,以及 CAIDA 的 plot-latlong。从您最喜欢的 CPAN 镜像下载并安装 GD 模块。您还需要安装 Geo::Coder:Yahoo Perl 模块及其依赖关系:Yahoo::Search。

  在把 plot-latlong 压缩包解压缩后,请把 .mapimages 目录和 .mapinfo 文件复制到 ${HOME} 目录中。我们稍后将在多次地图生成步骤中使用这些文件和目录。

  地理编码物理地址

  出于本文的目的,地理编码 被定义为查找给定地址的纬度和经度。可以用 plot-latlong 创建的大多数有用的数据地图将把详细信息解析为详细的城市或郊区。给定一组地址,我们将查找它们的纬度和经度,然后使用 plot-latlong 把它们显示到世界地图中。考虑以下地址列表:

  清单 1. 示例 locationNames 数据文件

cary, nc
oswego, ny
potsdam, germany
beijing, china
london, england
bangalore, india
brasilia, brazil

  我们将使用的 geocoder 模块和查找服务非常灵巧,因此它知道我们是指美国北卡罗来纳州的凯利市,而不是指新喀里多尼亚的一个名叫卡里的人。考虑用以下脚本来处理 locationNames 数据文件并创建地理编码的数据集:

  清单 2. yahooGeoCode.pl

#!/usr/bin/perl -w
# yahooGeoCode.pl - lookup long/lat of an address using yahoo
use strict;
use Geo::Coder::Yahoo;
my $geocoder = Geo::Coder::Yahoo->new(appid => 'my_app' );
while( my $line = <STDIN> )
{
 chomp($line);
 my $location = "";
 $location = $geocoder->geocode( location => "$line" );
 for ( @{$location} )
 {
  my %hash = %{$_};
  print "$hash{latitude} $hash{longitude} # $line n";
 }
}#while stdin

  用命令 cat locationNames | perl yahooGeoCode.pl >cityCoords 运行 geocoder。这将给您提供一个包含经度、纬度和城市名称的文件。您可以使用任何一个所需的 geocoder 来生成 cityCoords 中所需格式的文件。本文选用的是 Ask Bjoern Hansen 提供的 Yahoo/Perl 地理编码模块,是因为它十分优秀并且简单易用。

  清单 3. 示例 cityCoords 数据文件

35.791458 -78.781174 # cary, nc
43.455471 -76.510048 # oswego, ny
52.400002 13.07 # potsdam, germany
40.25 116.5 # beijing, china
51.52 -0.1 # london, england
12.97 77.559998 # bangalore, india
-15.78 -47.91 # brasilia, brazil

  用 plot-latlong 进行地图注释

  plot-latlong 的标准用法

  使用 cityCoords 文件中的地理编码记录,您现在可以使用 plot-latlong 程序把它们标绘到世界地图上。确保 plot-latlong.pl 程序位于当前目录中并用命令 cat cityCoords | perl plot-latlong.pl -s 5 >cityMap1.png 运行程序。这将生成一个标准的 plot-latlong 类型的图,其中大矩形表示城市所在位置。

  地图的注释

  GD 可用于为地图图像文件注释标题和出处文本、图例说明和灵活多变的边界。但是,plot-latlong 的实际功能是它能够返回地理位置的精确像素位置。使用这些像素坐标,我们可以绘制以这些点为起点的直线,重新设定直线的颜色、连接直线、用颜色填充周围的区域或者 GD 可以执行的任何其他操作。例如,我们需要在地图图像的顶部构建一条带图像标识符和文本的路线。从包含图像标识符和文本的每个框中,将向地图中相关的坐标绘制一条直线。清单 4 中说明的自定义 Perl 脚本将自动执行此过程。要生成所需的像素坐标,只需把 -c 选项传递给 plot-latlong,然后像素坐标将被打印到 stderr。再次运行标绘命令:cat cityCoords | perl plot-latlong.pl -s 5 -c >cityMap1.png 2>cityPixels。

  清单 4. worldCompositeMap.pl,第 1 部分

#!/usr/bin/perl -w
# worldCompositeMap.pl - overlay text, images and shapes on map image
use strict;
use GD;
die "specify: pixelFile dataFile sourceImage destImage" unless @ARGV == 4;
my $newMap = newFromPng GD::Image( $ARGV[2] );
my $imageWidth = $newMap->width();
my $imageHeight = $newMap->height();
my $topMargin = 110;
my $blankMap = new GD::Image($imageWidth, $imageHeight $topMargin);
my $white = $blankMap->colorAllocate(255,255,255);
my $black = $blankMap->colorAllocate(0,0,0);
$blankMap->copy( $newMap, 0,$topMargin, 0,0, $imageWidth,$imageHeight );
my @pixels = ();
my @data = ();
open( PIXELFILE, "$ARGV[0]" ) or die "can't open pixels file";
 while( my $line = <PIXELFILE> ){ push @pixels, $line }
close(PIXELFILE);
open( DATAFILE, "$ARGV[1]" ) or die "can't open data file";
 while( my $line = <DATAFILE> ){ push @data, $line }
close(DATAFILE);
my $posCount = 0;
my $textX = 25;
my $textY = 20;
my $annotateRowIncrement = 150;

  worldCompositeMap.pl 程序中的第一部分将检查命令行并且设置背景图像。$blankMap 变量是作为一个新的 GD Image 创建的,其宽度与输入图像相同,顶部有额外的空白用于保存注释信息。然后指定黑色和白色以确保按照预期为图像着色。原始的 plot-latlong 输出地图随后被复制到我们的 blankMap 中,向下移动了 110 像素。

  下一部分将把像素坐标装入 @pixels 数组。这些像素值将在 worldCompositeMap.pl 代码的第 2 部分中处理。将以相同方式执行用信息(本例中为位置名称和注释图像名)装入 @data 数组。请注意,这些文件都有相应的顺序入口。像素数据文件中的第 1 行将指定在城市数据文件的第 1 行中指定的地点的坐标。worldCompositeMap.pl 程序的第 2 部分将迭代数据文件并为图像加注。

  清单 5. worldCompositeMap.pl,第 2 部分

for my $dataLine ( @data )
{
 my( undef, undef, $pointX, $pointY) = split " ", $pixels[$posCount];
 my( $text, $faceImgName ) = split '##', $dataLine;
 $blankMap->stringFT( $black, '/usr/share/fonts/bitstream-vera/Vera.ttf',
  8,0,$textX,$textY, "$text",
  {  linespacing=>0.6,
    charmap => 'Unicode',
  });
 $blankMap->rectangle( $textX-10, 5, $textX 120, 100, $black);
 $blankMap->line($textX 50, 100, $pointX, $pointY $topMargin, $black );
 my $faceImg = newFromPng GD::Image( $faceImgName );
 $blankMap->copyResized( $faceImg, $textX,$textY 10, 0,0, 60,60, 115,115 );
 $textX = $annotateRowIncrement;
 $posCount ;
}#for each data line
open( TILEOUT,"> $ARGV[3]") or die "can't write $ARGV[3]";
 print TILEOUT $blankMap->png;
close(TILEOUT);

  对于数据数组中的每一行,提取图像中的精确像素坐标。从数据数组中采集指定的文本和注释图像文件名,并把指定的文本写入到图像中。当把文本绘制到图像上后,请用一个细黑框框起该文本,然后绘制一条从框的底部到相关数据点的直线。

  接下来将把注释图像调整后的版本装入并复制到指定文本下面的框中。然后,把 “当前” 位置朝图像的右侧增加指定的长度,并重复执行此过程。在写出图像后,程序将终止,生成以下输出:

  图 1. worldCompositeMap.pl 输出示例

使用 Perl、GD 和 plot-latlong 创建数据的地理标绘

  要构建图 1 中所示的图像文件,请运行命令 perl worldComposite.pl cityPixels locationNames cityMap1.png worldCityMap_done.png。

  用 plot-latlong 实现描绘主体的色彩渐变

  一般策略

  除了简单地标绘地理点外,为美国地图中各个州显示色彩渐变效果也是一种提供信息的有效方法。以下代码和过程描述将展示一种相对简单的方法,用于修改地图图像和 plot-latlong 程序,使您可以创建有用的具有色彩渐变效果的美国地图。

  地图图像修改

  要使用简单的 GD->Fill 命令实现州(或任何其他描绘的主体)的色彩渐变效果,该地域的内域必须毗邻。如果曾经使用过 Redmond Paint 或 The Gimp 中的 “Paint Bucket” 工具,则会熟悉这种工具的工作原理。

  特别地,对于州地图,地图上的州界必须经过修改才能与填充命令良好地结合使用。可以结合 Edge Enhance 效果和州界的像素级别的编辑来完成修改。确保密西根上半岛与马里兰的半数土地相连。您可以自己执行这些修改,也可以使用提供的图像和坐标文件来填充各个州。坐标文件 “state.coords” 只是一个 GD->Fill 命令的起始经度/纬度对(通常是州的首府)。要创建具有色彩渐变效果的特定地理区域地图或整张世界地图,您将需要确保进行图像编辑的区域都是毗邻的,并且为每个渐变的地区开发一个坐标文件。

  修改配置文件

  把修改后的美国各州的图像文件 (statemap.png) 从 下载 部分复制到 ~/.mapimages 目录中。接下来,您将需要修改 ~/.mapinfo 文件,让 plot-latlong 了解新地图的信息。把下面几行复制到 ~/.mapinfo 文件中:

  清单 6. ~/.mapinfo 配置

MAP statemap statemap.png 50 -125 24 -66
MAP temporary temporary.png 50 -125 24 -66
PROJECTION statemap ALBER 2812.7 30.8 45.5 21.7 -99.9 929 1561
PROJECTION temporary ALBER 2812.7 30.8 45.5 21.7 -99.9 929 1561

  标绘 plot-latlong 修改

  在合并州的过程中,将创建 temporary.png 文件。请注意,statemap.png 文件是基于 USA200.png 地图的。如果按需生成可视化地图(例如,在 Web 页面中),您可能会发现使用较小的地图对于缩短处理时间十分有用。对于新增的区别,plot-latlong 程序需要经过修改才能绘制蓝色圆圈而不绘制红色方框。这将提供另外一个级别的信息并且使用 GD 能够很轻易完成。在 plot-latlong 中,注释掉清单 7 中的代码行并插入清单 8 中的代码行。

  清单 7. plot-latlong 正方形

  $map->filledRectangle($left_x, $top_y,
    $left_x $point_size - 1, $top_y $point_size - 1,
    $red);

  清单 8. plot-latlong 圆形

  my $blue = $map->colorAllocate(64,64,255);
  $map->filledEllipse( $left_x ($point_size/2), $top_y ($point_size/2),
     $point_size, $point_size,
     $blue)

  色彩渐变的 plot-latlong 修改

  把 plot-latlong 文件复制到名为 fillState_plot-latlong 的文件中。打开此文件并删除清单 8 中的代码行。在同一位置中插入清单 9 中的代码行来填充毗邻区域,而不要绘制椭圆形或矩形。

  my $color = $map->colorAllocate(255, 255 - $value, 8);
  $map->fill( $left_x, $top_y, $color);

  您还需要把代码行 my ($lat, $long) = split /s /; 修改为 my ($lat, $long, $value) = split /s /;。这一行更改将允许读入百分数值,该值将决定上面显示的渐变百分数。

  示例百分数数据文件

  要有效地使用 fillState_plot-latlong 代码,必须构造一个适当的数据文件。完成此操作的一种简单方法是缩小要表示的数据范围,让其范围全部能用 0 到 100 的值来表示。在本例中,值 0 将生成一种较深的橙色,而值 100 将为普通的橙黄色。这种方法将提供每个州的大小的相对直观的信息,并为信息型渐变提供合理的范围。清单 9 将用 0 到 100 的百分数来显示与每个州相关的数据。

  清单 9. 示例 usStatePercs 文件

32.22434 -86.20379 75 #../../data/coords/AL.MONTGOMERY_
44.33064 -69.72971 98 #../../data/coords/ME.AUGUSTA_
33.51623 -112.02711 7 #../../data/coords/AZ.PHOENIX_
34.72240 -92.35407 11 #../../data/coords/AR.LITTLE_ROCK_
...

  使用 参考资料 中提供的 state.coords 文件或在每个州界内部创建您自己的一列坐标。您可以用以下命令构建类似清单 9 的样例百分数文件:

cat state.coords |
perl -lane '$r=int(rand(99));print "$F[0] $F[1] $r $F[2]"' >usStatesPercs

  usStatesPercs 文件现在包含填充的起始位置的坐标,以及相关州的相应渐变百分数。下一项任务是用清单 10 中所示的命令构建标绘了城市的美国各州渐变并加了注释的地图。第一条命令将构建色彩渐变的美国各州地图。第二条命令将把此地图复制到 ~/.mapimages 目录中,以用作下一步的基准地图。类似于标绘上面的世界地图,后续命令将在州渐变地图中标绘大型蓝色椭圆形,同时在图像中记录各自的像素坐标。使用这些像素坐标用于渐变标绘的地图中,随后图像将被加注普通框、图像和直线。

  清单 10. 用于构建州渐变、加注的合成地图的命令

cat usStatesPercs | perl fillState_plot-latlong -m statemap -s 7 >usStates1.png
cp usStates1.png ~/.mapimages/temporary.png
cat usStatesCoords |
 perl plot-latlong -m temporary -s 20 -c >usStates2.png 2>usStatesPixels
perl stateCompositeMap.pl usStatesPixels stateData
 usStates2.png usStatesFinal.png

  通过清单 10 中的命令创建的输出图像:

  图 2. stateCompositeMap.pl 输出示例

使用 Perl、GD 和 plot-latlong 创建数据的地理标绘

  stateCompositeMap.pl 程序可从 下载 部分下载。stateCompositeMap.pl 与 worldCompositeMap.pl 程序非常相似 —— 其修改主要是在把各种注释组件与各种尺寸的基本图像对齐。

  结束语

  使用这些技术、地理编码模块和地图工具,您有无数种选择可以创建按地区色彩渐变和按城市标绘的地图。考虑修改 plot-latlong 数据文件以读入除了数据值和位置以外的各种因素,并且可以创建更多不同种类的地图。使用 GD 在地图上的特定点绘制饼图而不绘制矩形或椭圆形。在像素坐标与 GD 之间创建连接,并填充出差销售人员所跨越的地区。考虑在该地理点根据组织中的员工数围绕指定城市进行阿尔法混合辐射式扫描。使用提供的像素数据来自动创建各个地区的可单击图像地图。随时间变化标绘数据,并使用 mencoder 创建图像的动态影像,等等。plot-latlong 和 GD 几乎提供了无数的方法实现数据可视化。