GeoHexV2SuperOverlay

GeoHexV2 で KML SuperOverlay を実現してみました。

the code

# -*- coding: utf-8 -*-
#server.rb
require 'rubygems'
require 'sinatra/base'
require 'haml'
require 'gh.rb' # @see http://d.hatena.ne.jp/hfu/20110224
require 'ostruct'

HOST = 'localhost'
PORT = 8092

module GeoHex # ヘックスの包含矩形が得られるように拡張
  class Zone
    def bbox
      coords = self.hexCoords
      @bbox = OpenStruct.new(:north => coords[1][:lat],
                             :south => coords[4][:lat],
                             :east => coords[3][:lon],
                             :west => coords[0][:lon])
    end

    def north
      bbox unless @bbox
      @bbox.north
    end

    def south
      bbox unless @bbox
      @bbox.south
    end

    def east
      bbox unless @bbox
      @bbox.east
    end

    def west
      bbox unless @bbox
      @bbox.west
    end

  end
end
                     
class App < Sinatra::Base
  get '/' do # 日本を包含できるヘックスを返す
    redirect "/geohex/#{GeoHex::Zone.new(20.0, 140.0, 2).code}"
  end

  get '/geohex/:geohex' do |geohex|
    content_type 'application/vnd.google-earth.kml+xml' # easier than apache!
    #content_type 'text/plain' # good for testing purpose

    current_geohex = GeoHex::Zone.new(geohex)
    coords = current_geohex.hexCoords
    coords << coords[0] # cause a polygon has to go around

    hexcenter_lat = GeoHex::Zone.decode(geohex)[0]
    hexcenter_lng = GeoHex::Zone.decode(geohex)[1]
    children = 
      [GeoHex::Zone.new(hexcenter_lat, hexcenter_lng,
                        current_geohex.level + 1)]
    3.times {|i|
      children << 
      GeoHex::Zone.new((coords[i][:lat] + coords[i + 1][:lat]) / 2, 
                       (coords[i][:lon] + coords[i + 1][:lon]) / 2, 
                       current_geohex.level + 1)
    }
    children.map! {|child|
<<-EOS
    %NetworkLink
      %name #{child.code}
      %Region
        %Lod
          %minLodPixels 128
          %maxLodPixels -1
        %LatLonAltBox
          %north #{child.north}
          %south #{child.south}
          %east #{child.east}
          %west #{child.west}
      %Link
        %href http://#{HOST}:#{PORT}/geohex/#{child.code}
        %viewRefreshMode onRegion
EOS
    }

    haml <<-EOS
%kml(xmlns='http://earth.google.com/kml/2.0')
  %Document
    %Region
      %LatLonAltBox
        %north #{current_geohex.north}
        %south #{current_geohex.south}
        %east #{current_geohex.east}
        %west #{current_geohex.west}
    %Style(id='geohexpolygon')
      %LineStyle
        %color ff0000ff
        %width 2
      %PolyStyle
        %fill 0
    %Placemark
      %name #{geohex}
      %styleUrl #geohexpolygon
      %Polygon
        %extrude 1
        %outerBoundaryIs
          %LinearRing
            %coordinates #{coords.map{|h| "#{h[:lon]},#{h[:lat]}"}.join(" ")}
#{children}
    EOS
  end
end

App.run! :port => PORT

the usage

$ ruby server.rb
== Sinatra/0.9.4 has taken the stage on 8092 for development with backup from Thin
>> Thin web server (v1.2.5 codename This Is Not A Web Server)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:8092, CTRL+C to stop

↑このようにしてサーバを立ち上げておいて、http://localhost:8092 にアクセスすると、日本を包含するヘックスについて、SuperOverlay が開始されます。

なぜ上側4ヘックスしか呼ばないのか。

コードに「3.times {|i|」(60行目付近)とあるように、レベルを上げるにつれて、上側4ヘックス(自分の直下1ヘックスプラスその上側3ヘックス)しか呼んでいません。この理由を説明します。
これは、下側の3ヘックスも合わせて合計7ヘックス呼んでしまうと、呼び出しの重複が発生してしまうからです。呼び出しの重複が呼び出しの重複を呼んで、ねずみ算的に呼び出しが増えてしまいます。自分の直下及び上側4ヘックスの合計4ヘックスのみを呼ぶことによって、重複なく呼び出すことができます。
4ヘックスを呼べば重複も不足もないことは、面積を考えれば説明できます。GeoHex V2 では、レベルを上げるにつれてサイズが 1/2 になると言います。面積としては 1/2 の二乗の 1/4 です。そのため、レベルが下のタイルを 4 つ呼ぶことで、 1/4 * 4 = 1 = 100% の面積のレベル下タイルを呼び出すことになります。そのため、レベル変更をしても面積カバー率が変化せず、継続的に SuperOverlay できることになります。