土地利用3次メッシュ
国土交通省のGISのサイトに「土地利用3次メッシュ」なるものがあったので、最新版をダウンロードしてマップとして表示してみた。
1kmメッシュと書いてあるので、かなり大きな範囲での情報なので使えない。
80x80のサイズ。
ソースコードはコレ。 ファイル名は埋め込み。
# coding: utf-8 import xml.etree.ElementTree as ET import numpy as np import matplotlib.pyplot as plot import sys JPGIS_FILE = "JPGIS/5340/L03-a-14_5340-jgd_GML/L03-a-14_5340.xml" tree = ET.parse(JPGIS_FILE) root = tree.getroot() for e in root.getiterator(): print(e.tag) tl = root.find('./{http://nlftp.mlit.go.jp/ksj/schemas/ksj-app}LanduseMesh/{http://nlftp.mlit.go.jp/ksj/schemas/ksj-app}coverage/{http://www.opengis.net/gml/3.2}rangeSet/{http://www.opengis.net/gml/3.2}DataBlock/{http://www.opengis.net/gml/3.2}tupleList') if tl is None: raise Exception("{http://www.opengis.net/gml/3.2}tupleList is not found") temp = np.array([[107, 66, 0], [221, 148, 0], [71, 104, 33], [255, 208, 138], [196, 44, 0], [162, 162, 162], [116, 116, 116], [48, 48, 48], [132, 183, 255], [72, 157, 255], [0, 86, 185], [163, 215, 113], [255, 255, 255]], dtype=float) temp = temp / 255.0 lines = tl.text.split() array = np.zeros((6400, 3)) i = 0 for l in lines: d = np.array(l.split(","), np.float) s = sum(d) d = d / s total = np.zeros((3), dtype=float) for j in range(13): t = d[j] * temp[j] total = total + t array[i] = total i += 1 print(array) array = array.reshape((80, 80, 3)) print(array) plot.imshow(array, interpolation='nearest') plot.show()
色分けは次の通り。
解析範囲外は白。
参考にしたサイト
木を生やす(5)
「木を生やす(1)」で書いた「木を生やす」意味の1.の方「木を生やす場所を決める」を考える。
木を生やす場所を決める方法として、1つには地形から判断する方法がある。次の2つの画像はだいたい同じ範囲の標高データとGoogleマップの衛星写真だけど、だいたい傾斜のキツいところに木が生えている。
ただ、この方法だと平野部の公園とか防風林、防砂林などは判断できない。
他に土地の利用区分みたいなデータがあれば、それで森林とか雑木林とか分かればと思う。
それに道路や鉄道などもそういうデータがありそうだし。
木を生やす(4)
wood.pyをモジュール化してpycraパッケージに追加したい。
そして、他のスクリプトから使えるようにしたい。
pycra/wood.pyとして保存。
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import math import random import sys def leaves(mc, x, y, z, radius, density): min_x = int(x - radius) max_x = int(x + radius) min_y = int(y - radius) max_y = int(y + radius) min_z = int(z - radius) max_z = int(z + radius) for dy in range(min_y, max_y+1): for dx in range(min_x, max_x+1): for dz in range(min_z, max_z+1): xx = dx - x yy = dy - y zz = dz - z s = xx * xx + yy * yy + zz * zz sq = math.sqrt(s) if sq <= radius: block_id = mc.getBlock(dx, dy, dz) if block_id == block.AIR: if sq < radius - 0.5: if random.random() < density: mc.setBlock(dx, dy, dz, block.LEAVES) else: mc.setBlock(dx, dy, dz, block.LEAVES) def wood(mc, x, y, z): height = 4 + int(random.random() * 5) mc.setBlocks(x, y, z, x, y + height, z, block.WOOD) offset = height * 0.25 leaves(mc, x, y + height - offset, z, height * 0.5, 0.5) if __name__ == '__main__': # ここからスタート mc = minecraft.Minecraft() if len(sys.argv) < 4: sys.exit("Usage: wood x y z") x = float(sys.argv[1]) y = float(sys.argv[2]) z = float(sys.argv[3]) wood(mc, x, y, z)
Pythonメモ
よく見るif __name__ == '__main__':
という形式を試してみた。
呼び出す側
本を読み返してたら、剣で右クリックしたところを検出するスクリプトがあったので、それを参考にして剣で右クリックしたところに木を生やすスクリプトを書いた。
mkwood.pyとして書いた。
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import pycra.wood as wd mc = minecraft.Minecraft() mc.events.clearAll() while True: blockHits = mc.events.pollBlockHits() if blockHits: blockHit = blockHits[0] if blockHit.face == 1: x = blockHit.pos.x y = blockHit.pos.y z = blockHit.pos.z blockId = mc.getBlock(x, y, z) while blockId != block.AIR: y = y + 1 blockId = mc.getBlock(x, y, z) wd.wood(mc, x, y, z)
一回叩いただけでも複数のイベントが発生するので、if blockHit.face == 1:
というので一回だけ処理するようにした。
if blockHit.face == 1:
は(多分)ブロックの上面を叩いたという判定。
実行結果
以前、読み込んだ地形に木を生やしてみた。
結構、離して植えたつもりが案外こんもりしちゃう。
木を生やす(3)
コマンド1つで幹も生やして葉っぱも生成したい。
leaves.pyを関数にして、木(幹)の位置と高さを指定して、それに合わせて葉っぱを生成する。
wood.pyとして、こんな感じに実装。
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import math import random import sys def leaves(mc, x, y, z, radius, density): min_x = int(x - radius) max_x = int(x + radius) min_y = int(y - radius) max_y = int(y + radius) min_z = int(z - radius) max_z = int(z + radius) for dy in range(min_y, max_y+1): for dx in range(min_x, max_x+1): for dz in range(min_z, max_z+1): xx = dx - x yy = dy - y zz = dz - z s = xx * xx + yy * yy + zz * zz sq = math.sqrt(s) if sq <= radius: block_id = mc.getBlock(dx, dy, dz) if block_id == block.AIR: if sq < radius - 0.5: if random.random() < density: mc.setBlock(dx, dy, dz, block.LEAVES) else: mc.setBlock(dx, dy, dz, block.LEAVES) def wood(mc, x, y, z): height = 4 + int(random.random() * 5) mc.setBlocks(x, y, z, x, y + height, z, block.WOOD) offset = height * 0.25 leaves(mc, x, y + height - offset, z, height * 0.5, 0.5) # ここからスタート mc = minecraft.Minecraft() if len(sys.argv) < 4: sys.exit("Usage: wood x y z") x = float(sys.argv[1]) y = float(sys.argv[2]) z = float(sys.argv[3]) wood(mc, x, y, z)
こんな感じに4本生成してみた。
/python wood 0 0 0 /python wood 5 0 0 /python wood 5 0 5 /python wood 0 0 5
配置が単純すぎた。
木を生やす(2)
leaves.pyを外はそのままで内部を間引くようにしてみた。
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import math import random import sys # ここからスタート mc = minecraft.Minecraft() if len(sys.argv) < 6: sys.exit("Usage: leaves x y z radius density") x = float(sys.argv[1]) y = float(sys.argv[2]) z = float(sys.argv[3]) r = float(sys.argv[4]) d = float(sys.argv[5]) min_x = int(x - r) max_x = int(x + r) min_y = int(y - r) max_y = int(y + r) min_z = int(z - r) max_z = int(z + r) for dy in range(min_y, max_y+1): for dx in range(min_x, max_x+1): for dz in range(min_z, max_z+1): xx = dx - x yy = dy - y zz = dz - z s = xx * xx + yy * yy + zz * zz sq = math.sqrt(s) if sq <= r: block_id = mc.getBlock(dx, dy, dz) if block_id == block.AIR: if sq < r - 0.5: if random.random() < d: mc.setBlock(dx, dy, dz, block.LEAVES) else: mc.setBlock(dx, dy, dz, block.LEAVES)
いい気になって、こんなパラメータで実行してみた。
/python leaves 0 100 0 90 0.3
こんなにデカいのね…
ブロックが葉で透過処理が入るからか、近寄るとCPUファンがガンガン回る。
で、当然、葉っぱだけなので枯れて崩壊する。
木を生やす(1)
木を生やしたい。
木を生やすには、次の2つの項目がある。
- 木を生やす場所を決める。
- 生やす木の形を決めて、実際にブロックを設置する。
木を生やす場所を決めるのは、またの機会にして、今回は木の形を決めて実際にブロックを設置する方を考える。
適切な高さ
適切な高さを見るために、1〜4個の木のブロックを積み上げてみた。
4個くらいの高さが無いと「木」として感じられない。
そのまま11個重ねる(/python setblocks.py 0 0 0 0 10 0 17
)と、1個のブロックの太さとの木としてはこれくらいが限界かな?
斧が届くのはここ(Y=6のブロック)まで。だいたいゲーム内で出てくるのも8くらいまでだと感じる。しかも、そこまで高い木は太さも4あるし。
結論
だいたい4〜8くらいが妥当。
葉の形
多分、木の高さに合わせて、葉の形も決まってくる。
まずは高さ4の木
幹4に葉3x3x3を中心3のところに配置してみた。
ちょっと葉がボリューム不足?
葉3x4x3にしてみた。
ちょっと面長?
葉の真ん中に5x2x5を追加。 こんもりし過ぎ。
角を削ってみる。 まぁ、ありそうだけど、もう1つ高さが欲しくなる。
あと、葉がみっちり詰まっている感じがするので、setblocksで隙間が空く(0.7くらい?)バージョンが欲しい。
バランス的には最初のが一番いいか。
高さ6の木
幹6葉5x5x5の木
右上の角が欠けているのは、勝手に欠け始めた。 隙間が空くバージョン+球形に設置できるといいなぁ。
leaves.pyとして書いてみた。
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import math import random import sys # ここからスタート mc = minecraft.Minecraft() if len(sys.argv) < 6: sys.exit("Usage: leaves x y z radius density") x = float(sys.argv[1]) y = float(sys.argv[2]) z = float(sys.argv[3]) r = float(sys.argv[4]) d = float(sys.argv[5]) min_x = int(x - r) max_x = int(x + r) min_y = int(y - r) max_y = int(y + r) min_z = int(z - r) max_z = int(z + r) for dy in range(min_y, max_y+1): for dx in range(min_x, max_x+1): for dz in range(min_z, max_z+1): xx = dx - x yy = dy - y zz = dz - z s = xx * xx + yy * yy + zz * zz if math.sqrt(s) <= r: block_id = mc.getBlock(dx, dy, dz) if block_id == block.AIR: if random.random() < d: mc.setBlock(dx, dy, dz, block.LEAVES)
結構、外側が欠けると美しくないのでdentisyには1.0を指定した。
/python setblocks 0 0 0 0 5 0 17 /python leaves 0 4 0 2.7 1.0
まずは第1段として。
Pythonメモ
乱数はrandomをインポートして、random.random()で[0.0-1.0)の範囲で乱数を返す。
平方根はmathをインポートして、math.sqrt()で計算する。
ワールドタイプ:スーパーフラットの世界
ワールドタイプ:スーパーフラットの世界はどうなっていて、どこまでできるのか?
特に高さと深さの制限を調査する。
初期値
項目 | 値 | 備考 |
---|---|---|
Block* | 817 4 245 | シード値:"Python World" |
Facing | South | 初期値 |
雲の中 | 127〜129 | 雲の高さ |
リスポーン位置 | 817 4 250 | 初回 |
Block*値はシード値が同じだと同じ値になる。
方角
方角 | 軸 |
---|---|
東 | X軸プラス |
西 | X軸マイナス |
南 | Z軸プラス |
北 | Z軸マイナス |
上 | Y軸プラス |
下 | Y軸マイナス |
地下
草、土、土、地殻の4つしかない。しかも、地殻も壊せるので、地殻を壊すと奈落の底へ落ちて死ぬ。
死ぬたびにリスポーン位置が変わる。
setBlock, setBlocksの座標系
今まで自分の位置とは関係ないグローバルな座標(と思って)を指定していたけど、自分の近くにブロックが設置されていた。 F3キーで表示されるBlockの座標とはかなり違う。 なので、setBlock, setBlocksの座標系がどうなっているのか調べる。
minecraftのAPIのgetBlock, setBlock, setBlocksをコールするだけのスクリプトを書いて、それをコールしてみる。
getblock.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "getBlock({0:d}, {1:d}, {2:d})={3:d}" mc = minecraft.Minecraft() if len(sys.argv) < 4: sys.exit("getblock.py [x y z]") pos_x = int(sys.argv[1]) pos_y = int(sys.argv[2]) pos_z = int(sys.argv[3]) blockId = mc.getBlock(pos_x, pos_y, pos_z) mc.postToChat(FORMAT.format(pos_x, pos_y, pos_z, blockId))
setblock.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "setBlock({0:d}, {1:d}, {2:d}, {3:d})" mc = minecraft.Minecraft() if len(sys.argv) < 5: sys.exit("setblock.py [x y z b]") pos_x = int(sys.argv[1]) pos_y = int(sys.argv[2]) pos_z = int(sys.argv[3]) block_id = int(sys.argv[4]) mc.setBlock(pos_x, pos_y, pos_z, block_id) mc.postToChat(FORMAT.format(pos_x, pos_y, pos_z, block_id))
setblocks.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "setBlocks({0:d}, {1:d}, {2:d}, {3:d}, {4:d}, {5:d}, {6:d})" mc = minecraft.Minecraft() if len(sys.argv) < 5: sys.exit("setblocks.py [x1 y1 z1 x2 y2 z2 b]") pos1_x = int(sys.argv[1]) pos1_y = int(sys.argv[2]) pos1_z = int(sys.argv[3]) pos2_x = int(sys.argv[4]) pos2_y = int(sys.argv[5]) pos2_z = int(sys.argv[6]) block_id = int(sys.argv[7]) mc.setBlocks(pos1_x, pos1_y, pos1_z, pos2_x, pos2_y, pos2_z, block_id) mc.postToChat(FORMAT.format(pos1_x, pos1_y, pos1_z, pos2_x, pos2_y, pos2_z, block_id))
どうも関係がよくわからないので、playerの位置に関するAPIも実装してみた。 player.getPos(), player.getTitlePos(), player.setPos(...), player.setTitlePos(...) の4つ。
getpos.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "player.getPos()=({0:f}, {1:f}, {2:f})" mc = minecraft.Minecraft() if len(sys.argv) > 1: sys.exit("Usage: getpos.py") pos = mc.player.getPos() mc.postToChat(FORMAT.format(pos.x, pos.y, pos.z))
gettilepos.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "player.getTilePos()=({0:f}, {1:f}, {2:f})" mc = minecraft.Minecraft() if len(sys.argv) > 1: sys.exit("Usage: gettilepos.py") pos = mc.player.getTilePos() mc.postToChat(FORMAT.format(pos.x, pos.y, pos.z))
setpos.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "player.setPos({0:f}, {1:f}, {2:f})" mc = minecraft.Minecraft() if len(sys.argv) < 4: sys.exit("Usage: setpos.py [x y z]") pos_x = float(sys.argv[1]) pos_y = float(sys.argv[2]) pos_z = float(sys.argv[3]) mc.player.setPos(pos_x, pos_y, pos_z) mc.postToChat(FORMAT.format(pos_x, pos_y, pos_z))
settilepos.py
# coding: utf-8 import mcpi.minecraft as minecraft import mcpi.block as block import sys FORMAT = "player.setTilePos({0:f}, {1:f}, {2:f})" mc = minecraft.Minecraft() if len(sys.argv) < 4: sys.exit("Usage: settilepos.py [x y z]") pos_x = float(sys.argv[1]) pos_y = float(sys.argv[2]) pos_z = float(sys.argv[3]) mc.player.setTilePos(pos_x, pos_y, pos_z) mc.postToChat(FORMAT.format(pos_x, pos_y, pos_z))
試しにplayer.getPos()を実行してみるとわかった!
F3キーで表示される座標系とは違う、独自の座標系で動いていた。
これはクリエイティブモードのスーパーフラットだけじゃなく、サバイバルモードでもそうだった。
だいたい、自分の初期位置は0 0 0の近く(だいたい4マス以内)になる。
だから初期位置にいればスクリプトで0 0 0原点でsetBlockしても見える範囲に設置される。
スーパーフラットでは、0 0 0は地表の1つ上。ここに何かを設置すると、「地表面に何かを置いた状態」になる。
上空の制限
高さ制限は255がMAXのもよう。それを超えるとF3キーの画面でOutside of world...と表示される。
setBlockでは251より上に設置できない。 試しにそのブロックの上に立ったらF3キーの画面のBlockの項のY値が256になった。
地下の制限
地下にsetBlock, setBlocksで-4以上の深さにはブロックを設置できない。