六角形(geohex)で地図をうめる その1

無性に六角形(geohex)で地図を埋め尽くしてみたくなったので、2週間ほど前からtwitterのstreamを使ってこの一週間でtrendに上がっているような流行りキーワードを位置情報付きでつぶやいているものをトラックしてDBに突っ込んでました。

ちなみにtwitterのstreamでtrackする場合は400wordsぐらいが限界のようです。

で、それをそのままgooglemaps上にgeohexを利用してマッピングします。

だいたい見てみると、ロンドン辺りと西海岸、東海岸辺りが埋まっていました。

一応これはキーワード事に色を変えているのですが、量が分からないので次にgoogleearthにマッピングをします。HexTweetMapっていうのがgooglemapsの方にマッピングする時に使ったプロジェクトで、ark使ったんでmodels->get('teng') という事になってます。

で、別に六角形の内包関係等々の処理とかはいらないので既存のgeohex v2でもいいのですが、後で思いついた時の事も考えておいてgeohex v3を使います。v3を含んでいるperlにポートされてるものは makamaka/geohex-perl · GitHub にあります。

で、 を見ると、kmlでポリゴンも簡単に書けるって事が分かったのでそのようなxmlを吐くようにします。

ちなみに geohex のライセンスは以下のとおりです。

#GeoHex by @sa2da (http://geogames.net) is licensed under Creative Commons BY-SA 2.1 Japan License.

use strict;
use warnings;

use lib qw/lib/;
use HexTweetMap::Models;

use Geo::Hex;
use Path::Class;
use XML::LibXML;

my $geohex = Geo::Hex->new( version => 3 );

my $dom = XML::LibXML::Document->new('1.0', 'utf-8');
my $kml = $dom->createElement('kml');
$kml->setAttribute('xmlns' => 'http://earth.google.com/kml/2.0');
$dom->setDocumentElement( $kml );

my $document = $dom->createElement('Document');
$kml->appendChild( $document );

my $folder = $dom->createElement('Folder');
$document->appendChild( $folder );

my $map = create_map();

for my $code (keys(%{ $map })) {
    my $placemark = create_placemark_node( dom => $dom, name => $code, description => $code, %{$map->{$code}} );
    $folder->appendChild($placemark) if $placemark;
}

my $file = file('tmp', 'hexmap.kml');
my $fh = $file->openw or die $!;
$fh->print($dom->toString( 1 ));
$fh->close;

sub create_placemark_node {
    my %attr = @_;
    my $dom = $attr{dom};

    my $placemark = $dom->createElement('Placemark');

    my $name = $dom->createElement('name');
    $name->appendText($attr{name});
    $placemark->appendChild( $name );

    my $desc = $dom->createElement('description');
    $desc->appendText($attr{description});
    $placemark->appendChild( $desc );

    my $polygon = $dom->createElement('Polygon');
    $placemark->appendChild( $polygon );

    my $extrude = $dom->createElement('extrude');
    $extrude->appendText(1);
    $polygon->appendChild($extrude);

    my $altitudeMode = $dom->createElement('altitudeMode');
    $altitudeMode->appendText('relativeToGround');
    $polygon->appendChild($altitudeMode);

    my $outerBoundaryIs = $dom->createElement('outerBoundaryIs');
    $polygon->appendChild( $outerBoundaryIs );

    my $linearRing = $dom->createElement('LinearRing');
    $outerBoundaryIs->appendChild( $linearRing );

    my $coordinates = $dom->createElement('coordinates');
    my $str;
    my $coords = $attr{hex}->hex_coords();
    return if ( ( $coords->[4]->[0] > 85.051128514 ) or ( $coords->[1]->[0] < -85.051128514 ) );

    for my $coord (@{ $coords }) {
        $str .= sprintf "%f,%f,%d\n", $coord->[1], $coord->[0], $attr{count} * 100;
    }

    $coordinates->appendText($str);
    $linearRing->appendChild( $coordinates );

    $placemark;
}

sub create_map {
    my $map = {};

    my $teng = HexTweetMap::Models->get('teng');
    my @tweets = $teng->search_by_sql(q{select * from tweet});

    for my $tweet (@tweets) {
        my $zone = $geohex->to_zone( $tweet->lat, $tweet->lng, 4 );

        unless ( $map->{$zone->code} ) {
            $map->{$zone->code} = {
                hex => $zone,
                count => 1,
            };
        }
        else {
            $map->{$zone->code}->{count}++;
        }
    }

    $map;
}

すると、


(ロンドン辺り)

(西海岸辺り)

(東海岸辺り)

のような結果になりました。多分、ロンドンの出っ張ってる所は、trendなキーワードをつぶやくspamだかbotだかがあるんでしょう。

しばらくトラックしてもう少し面白い地図とか作れたらと思いますが、なんだか六角形が地図の上にマップされてるのって興奮しますね。