6

[lat、lon、density]タプルの形式でいくつかの特定のデータセットのヒートマップスタイルのレンダリングを表示する静的KML(Google Earthマークアップ)ファイルを作成しようとしています。

私が持っている非常に単純なデータセットは、人口密度に関するものです。

私の要件は次のとおりです。

  • 特定の緯度、経度のデータを入力できる必要があります
  • その緯度、経度でのデータの密度を指定できる必要があります
  • KMLにエクスポートする必要があります

他の場所で使用されるKMLを構築するためにこれらのファイルをオフラインで生成するため、このプロジェクトの要件は言語に依存しません。

いくつかのプロジェクト、特にheatmap.pyを見てきました。これは、KMLエクスポートを使用したPythonのgheatの移植版です。これまでに見つけたプロジェクトはすべて、アルゴリズムに入力された[lat、lon]ポイントの密度からヒートマップを構築することに依存しているという意味で、私はレンガの壁にぶつかりました。

[lat、lon]タプルだけをフィードするようにデータセットを適応させる明確な方法がないが、私が持っている密度値を使用してそれらをフィードする方法を調整する場合は、知りたいです!

4

3 に答える 3

5

ちょっとウィル、heatmap.pyは私です。あなたのリクエストは一般的なものであり、対処すべきもののリストに含まれています。一般的な方法でこれを行う方法はまだよくわかりません。heatmap.pyの用語でdotsizeは、現在のようにグローバルドットサイズではなく、ポイントごとに設定するのは簡単ですが、それが本当のニーズに対応できるかどうかはわかりません。2010年の夏のリリースを目指していますが、おそらくこのmodを自分で作成することもできます。

カーネル密度推定ツールを検索してみてください。それが統計家がヒートマップと呼んでいるものです。 Rには、ニーズをより迅速に満たすことができる、使用できる優れた組み込みツールがいくつかあります。

幸運を!

于 2010-04-17T14:07:23.807 に答える
2

スクリプトを更新してheatmap.py、各ポイントの密度を指定できるようにしました。変更をブログにアップロードしました。それがあなたが望むことを正確に行うかどうかはわかりません!

乾杯、アレックス

更新[2020年11月13日] しばらく前にブログをアーカイブしたため、リンクが機能しなくなったため、以下の変更点を参照してください。

差分ファイル:

--- __init__.py 2010-09-14 08:40:35.829079482 +0100
+++ __init__.py.mynew   2010-09-06 14:50:10.394447647 +0100
@@ -1,5 +1,5 @@
 #heatmap.py v1.0 20091004
-from PIL import Image,ImageChops
+from PIL import Image,ImageChops,ImageDraw
 import os
 import random
 import math
@@ -43,10 +43,13 @@
     Most of the magic starts in heatmap(), see below for description of that function.
     """
     def __init__(self):
+        self.minIntensity = 0
+        self.maxIntensity = 0
         self.minXY = ()
         self.maxXY = ()
+       
 
-    def heatmap(self, points, fout, dotsize=150, opacity=128, size=(1024,1024), scheme="classic"):
+    def heatmap(self, points, fout, dotsize=150, opacity=128, size=(4048,1024), scheme="classic", area=(-180,180,-90,90)):
         """
         points  -> an iterable list of tuples, where the contents are the 
                    x,y coordinates to plot. e.g., [(1, 1), (2, 2), (3, 3)]
@@ -59,33 +62,41 @@
         size    -> tuple with the width, height in pixels of the output PNG 
         scheme  -> Name of color scheme to use to color the output image.
                    Use schemes() to get list.  (images are in source distro)
+        area    -> specify the coordinates covered by the resulting image 
+                   (could create an image to cover area larger than the max/
+                   min values given in the points list) 
         """
-        
+        print("Starting heatmap")
         self.dotsize = dotsize
         self.opacity = opacity
         self.size = size
         self.imageFile = fout
- 
+
         if scheme not in self.schemes():
             tmp = "Unknown color scheme: %s.  Available schemes: %s"  % (scheme, self.schemes())                           
             raise Exception(tmp)
 
-        self.minXY, self.maxXY = self._ranges(points)
-        dot = self._buildDot(self.dotsize)
+        self.minXY = (area[0],area[2])
+        self.maxXY = (area[1],area[3])
 
+        self.minIntensity, self.maxIntensity = self._intensityRange(points)
+        
         img = Image.new('RGBA', self.size, 'white')
-        for x,y in points:
+        for x,y,z in points:
+            dot = self._buildDot(self.dotsize,z)
             tmp = Image.new('RGBA', self.size, 'white')
             tmp.paste( dot, self._translate([x,y]) )
             img = ImageChops.multiply(img, tmp)
 
-
+        print("All dots built")
         colors = colorschemes.schemes[scheme]
         img.save("bw.png", "PNG")
+        print("Saved temp b/w image")
+        print("Colourising")
         self._colorize(img, colors)
 
         img.save(fout, "PNG")
-
+        print("Completed colourising and saved final image %s" % fout)
     def saveKML(self, kmlFile):
         """ 
         Saves a KML template to use with google earth.  Assumes x/y coordinates 
@@ -110,17 +121,19 @@
         """
         return colorschemes.schemes.keys() 
 
-    def _buildDot(self, size):
+    def _buildDot(self, size,intensity):
         """ builds a temporary image that is plotted for 
             each point in the dataset"""
+        
+        intsty = self._calcIntensity(intensity)
+        print("building dot... %d: %f" % (intensity,intsty))
+
         img = Image.new("RGB", (size,size), 'white')
-        md = 0.5*math.sqrt( (size/2.0)**2 + (size/2.0)**2 )
-        for x in range(size):
-            for y in range(size):
-                d = math.sqrt( (x - size/2.0)**2 + (y - size/2.0)**2 )
-                rgbVal = int(200*d/md + 50)
-                rgb = (rgbVal, rgbVal, rgbVal)
-                img.putpixel((x,y), rgb)
+        draw  =  ImageDraw.Draw(img)
+        shade = 256/(size/2)
+        for x in range (int(size/2)):
+            colour = int(256-(x*shade*intsty))
+            draw.ellipse((x,x,size-x,size-x),(colour,colour,colour))
         return img
 
     def _colorize(self, img, colors):
@@ -139,7 +152,7 @@
                 rgba.append(alpha) 
 
                 img.putpixel((x,y), tuple(rgba))
-            
+     
     def _ranges(self, points):
         """ walks the list of points and finds the 
         max/min x & y values in the set """
@@ -153,6 +166,23 @@
             
         return ((minX, minY), (maxX, maxY))
 
+    def _calcIntensity(self,z):
+        return (z/self.maxIntensity)        
+               
+    def _intensityRange(self, points):
+        """ walks the list of points and finds the 
+        max/min points of intensity
+        """   
+        minZ = points[0][2]
+        maxZ = minZ
+        
+        for x,y,z in points:
+            minZ = min(z, minZ)
+            maxZ = max(z, maxZ)
+        
+        print("(minZ, maxZ):(%d, %d)" % (minZ,maxZ))
+        return (minZ, maxZ)
+        
     def _translate(self, point):
         """ translates x,y coordinates from data set into 
         pixel offsets."""

およびデモスクリプト:

import heatmap
import random
import MySQLdb
import math

print "starting script..."

db = MySQLdb.connect(host="localhost", # your host, usually localhost
                     user="username", # your username
                      passwd="password", # your password
                      db="database") # name of the data base
cur = db.cursor() 

minLng = -180
maxLng = 180
minLat = -90
maxLat = 90

# create and execute the query
query = "SELECT lat, lng, intensity FROM mytable \
            WHERE %f<=tllat AND tllat<=%f \
            AND %f<=tllng AND tllng<=%f" % (minLat,maxLat,minLng,maxLng)

cur.execute(query)

pts = []
# print all the first cell of all the rows
for row in cur.fetchall() :
    print (row[1],row[0],row[2])
    # work out the mercator projection for latitute x = asinh(tan(x1))
    proj = math.degrees(math.asinh(math.tan(math.radians(row[0]))))
    print (row[1],proj,row[2])
    print "-"*15
    if (minLat < proj and proj < maxLat):
        pts.append((row[1],proj,row[2]))

print "Processing %d points..." % len(pts)

hm = heatmap.Heatmap()
hm.heatmap(pts, "bandwidth2.png",30,155,(1024,512),'fire',(minLng,maxLng,minLat,maxLat))
于 2010-09-08T18:48:28.130 に答える
1

これを行う1つの方法は、各ポイントがそのポイントの密度に従って繰り返されるタプルの(より大きな)リストを作成することだと思います。密度の高いポイントは多数のポイントが重なり合って表されますが、密度の低いポイントはポイントが少なくなります。したがって、代わりに:(かなり鈍いデータセット)[(120.7, 82.5, 2), (130.6, 81.5, 1)]を使用します。[(120.7, 82.5), (120.7, 82.5), (130.6, 81.5)]

考えられる問題の1つは、密度が整数ではなく浮動小数点数である可能性があるため、データを正規化して丸める必要があることです。変換を行う1つの方法は、次のようなものです。

def dens2points (dens_tups):
    min_dens = dens_tups[0][2]
    for tup in dens_tups:
        if (min_dens > tup[2]):
           min_dens = tup[2]
    print min_dens

    result = []
    for tup in dens_tups:
        for i in range(int(tup[2]/min_dens)):
            result.append((tup[0],tup[1]))
    return result

if __name__ == "__main__":
    input = [(10, 10, 20.0),(5, 5, 10.0),(10,10,0.9)]
    output = dens2points(input)
    print input
    print output

(これはあまりPythonicではありませんが、単純なテストケースでは機能するようです)。このサブルーチンは、データをheatmap.pyで受け入れられる形式に変換する必要があります。少しの努力で、サブルーチンを2行に減らすことができると思います。

于 2010-03-31T20:01:38.927 に答える