{"id":1717,"date":"2013-07-28T18:33:05","date_gmt":"2013-07-28T18:33:05","guid":{"rendered":"http:\/\/davstott.me.uk\/?p=1717"},"modified":"2014-04-19T19:07:51","modified_gmt":"2014-04-19T19:07:51","slug":"raspberry-pi-drawing-maps-inside-minecraft","status":"publish","type":"post","link":"https:\/\/davstott.me.uk\/index.php\/2013\/07\/28\/raspberry-pi-drawing-maps-inside-minecraft\/","title":{"rendered":"Raspberry Pi &#8211; drawing maps inside Minecraft"},"content":{"rendered":"<p>A python Ordnance Survey map viewer, drawn inside Minecraft, on a Raspberry Pi. Just what the world needs&#8230; This will also work for Minecraft drawing pretty much any small image, such as a mugshot from a webcam.<\/p>\n<div id=\"attachment_1728\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1728\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955-300x225.jpg\" alt=\"Ordnance Survey map inside Minecraft Pi edition\" width=\"300\" height=\"225\" class=\"size-medium wp-image-1728\" srcset=\"https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955-300x225.jpg 300w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955-624x468.jpg 624w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955.jpg 800w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-1728\" class=\"wp-caption-text\">Ordnance Survey map inside Minecraft Pi edition<\/p><\/div>\n<p><!--more--><\/p>\n<p>This idea came to me after I attended the excellent Raspberry Jam in York the other month, a great presentation from <a href=\"http:\/\/www.stuffaboutcode.com\/2013\/06\/raspberry-jam-york-minecraft-programming.html\">Martin O&#8217;Hanlon<\/a> and after a chance remark from <a href=\"http:\/\/charlottegodley.co.uk\/wp\/?p=789\">@charwarz<\/a>  about drawing a rainbow inside Minecraft, Pi edition (hereafter MCPI). It&#8217;s really not all that complicated to do (although I might turn it into a full WMS viewer in the future) and it&#8217;s a good catalyst for illustrating how to interact with MCPI from Python and also a bit about image handling. It&#8217;s just taken me 6 weeks to find the time to write it up..<\/p>\n<p>The first step is to get your Raspberry Pi set up with Minecraft and the Python API to it. This is as straightforward as following the instructions at <a href=\"http:\/\/pi.minecraft.net\/\">http:\/\/pi.minecraft.net\/<\/a> to download and extract the tarball. It&#8217;s worth noting that this does need X windows, so you&#8217;ll need to either plug in a display to your PI or set up a remote X server.<\/p>\n<h2>Minecraft and Python API<\/h2>\n<p>MCPI has an easy to use protocol that listens on TCP socket 4711 by default, but we&#8217;ll be using the Python API to it because it saves us writing some not terribly interesting code.<\/p>\n<p>The API&#8217;s capabilities are defined in mcpi\/api\/spec\/mcpi_protocol_spec.txt, including a link to <a href=\"http:\/\/www.minecraftwiki.net\/wiki\/Data_values_(Pocket_Edition)\">http:\/\/www.minecraftwiki.net\/wiki\/Data_values_(Pocket_Edition)<\/a> for the values for blockType and blockData (more on this later). It&#8217;s worth noting that the PI coordinate system is cartesian, but uses X and Z for its ground plane and Y is the vector towards the sky. The origin (0,0,0) is the spawn point. <\/p>\n<p>Once minecraft-pi is running in a window, we can open up a terminal window, run python, and make it do our bidding.  To do that, fire up a terminal window and type:<\/p>\n<pre>\r\n  pi@raspberrypi:~\/$ cd mcpi\/api\/python\r\n  pi@raspberrypi:~\/$ python\r\n  >>> import mcpi.minecraft\r\n  >>> # open the connection to minecraft\r\n  >>> mc = mcpi.minecraft.Minecraft.create()\r\n  >>> # make some text display in the chat log\r\n  >>> mc.postToChat(\"Hello World!\")\r\n  >>> # get the player's current position\r\n  >>> mc.player.getPos()\r\n  >>> # set the block at position (1,1,1) to stone\r\n  >>> mc.setBlock(1, 1, 1, 1) \r\n<\/pre>\n<p>This is only some of the API&#8217;s capabilities, but we can do an awful lot with just this, such as drawing and animating real objects, making games or making glass tiles appear at the player&#8217;s feet, allowing them to free run through mid-air.<\/p>\n<h2>Image Quantising<\/h2>\n<p>This is the image we&#8217;re about to draw inside Minecraft. It&#8217;s from the <a href=\"http:\/\/www.ordnancesurvey.co.uk\/oswebsite\/products\/250k-raster\/index.html\">Ordnance Survey&#8217;s 1:250,000 raster map<\/a>, which is licensed under the <a href=\"http:\/\/www.ordnancesurvey.co.uk\/oswebsite\/products\/os-opendata.html\">OS Opendata licence<\/a>. From a human point of view, this is a section of a map showing the North East of the city of York, but from a computer&#8217;s point of view, it&#8217;s a 200 pixel square collection of numbers that can be drawn as colours on a screen.<\/p>\n<div id=\"attachment_1720\" style=\"width: 210px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1720\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york.png\" alt=\"OS 1:250,000 map of North East York. Contains Ordnance Survey data \u00c2\u00a9 Crown copyright and database right 2013\" width=\"200\" height=\"200\" class=\"size-full wp-image-1720\" srcset=\"https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york.png 200w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york-150x150.png 150w\" sizes=\"auto, (max-width: 200px) 100vw, 200px\" \/><\/a><p id=\"caption-attachment-1720\" class=\"wp-caption-text\">OS 1:250,000 map of North East York.<br \/>Contains Ordnance Survey data \u00c2\u00a9 Crown copyright and database right 2013<\/p><\/div>\n<p>According to the GIMP, this image contains 6902 unique colours, which is going to be difficult to represent within Minecraft because it doesn&#8217;t have blocks that come in 6902 different colours. It does, however, come with a set of wool blocks that come in 16 unique colours, which should do the job nicely. <\/p>\n<p>To transform one into the other, we need the Python Imaging Library (PIL) to do some work for us, quantising the RGB image down to match our wool blocks.  To do this, it needs to have a palette defined to match each block to its RGB colour.<\/p>\n<p>One way of working out the exact colour value for, say, the Lime Green wool block (TileData 5) would be to fish out its sprite from mcpi\/data\/images\/terrain.png and then take an average of all the pixels in it. That&#8217;s a fair amount of leg work, but luckily some generous people at <a href=\"http:\/\/teaminterrobang.com\/showthread.php?35959-Minecraft-wool-color-Photoshop-ACO-file\">http:\/\/teaminterrobang.com\/showthread.php?35959-Minecraft-wool-color-Photoshop-ACO-file<\/a> have already done it for us, so we can describe our palette in python like this:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/davstott\/6098768.js?file=colours.py\"><\/script><br \/>\n<noscript><\/p>\n<pre>\r\n# palette values taken from http:\/\/teaminterrobang.com\/showthread.php?35959-Minecra\r\nft-wool-color-Photoshop-ACO-file\r\n# these could also be worked out from averaging the colours from the sprites in mcp\r\ni\/data\/images\/terrain.png\r\n# and extended by including other block types, but 16 colours works ok\r\ncolours = {\"White\": {\"argb\": \"FFe4e4e4\", \"tileId\": 0},\r\n           \"Light Grey\": {\"argb\": \"FFa0a7a7\", \"tileId\": 8}, \r\n           \"Dark Grey\": {\"argb\": \"FF414141\", \"tileId\": 9},\r\n           \"Black\": {\"argb\": \"FF181414\", \"tileId\": 15},\r\n           \"Red\": {\"argb\": \"FF9e2b27\", \"tileId\": 14},\r\n           \"Orange\": {\"argb\": \"FFea7e35\", \"tileId\": 1},\r\n           \"Yellow\": {\"argb\": \"FFc2b51c\", \"tileId\": 4}, \r\n           \"Lime Green\": {\"argb\": \"FF39ba2e\", \"tileId\": 5},\r\n           \"Green\": {\"argb\": \"FF364b18\", \"tileId\": 13},\r\n           \"Light Blue\": {\"argb\": \"FF6387d2\", \"tileId\": 3},\r\n           \"Cyan\": {\"argb\": \"FF267191\", \"tileId\": 9},\r\n           \"Blue\": {\"argb\": \"FF253193\", \"tileId\": 11},\r\n           \"Purple\": {\"argb\": \"FF7e34bf\", \"tileId\": 10},\r\n           \"Magenta\": {\"argb\": \"FFbe49c9\", \"tileId\": 2},\r\n           \"Pink\": {\"argb\": \"FFd98199\", \"tileId\": 6},\r\n           \"Brown\": {\"argb\": \"FF56331c\", \"tileId\": 12}}\r\n<\/pre>\n<p><\/noscript><\/p>\n<p>The PIL function that converts our full colour image into one with a quantised palette looks like this:<\/p>\n<pre>\r\n  outputImage = Image.open(\"york.png\").convert(\"RGB\").quantize(palette = mcImage)\r\n<\/pre>\n<p>Where mcImage is a PIL image object with its palette set to our collection of colours. We convert our Python data structure into this image using this code:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/davstott\/6098768.js?file=paletteConvert.py\"><\/script><br \/>\n<noscript><\/p>\n<pre>\r\n# store the tileIDs by the offset in the resulting palette List for later lookup\r\ntileIdsByOffset = []\r\n# create our intermediate image to do nothing but hold our palette\r\nmcImage = Image.new(\"P\", (1, 1))\r\npalette = []\r\nfor name in colours:\r\n  val = colours[name][\"argb\"]\r\n  colours[name][\"r\"] = int(val[2:4], 16)\r\n  colours[name][\"g\"] = int(val[4:6], 16)\r\n  colours[name][\"b\"] = int(val[6:8], 16)\r\n  tileIdsByOffset.append(colours[name][\"tileId\"])\r\n  palette.extend((colours[name][\"r\"], colours[name][\"g\"], colours[name][\"b\"]))\r\n\r\npalette.extend((0,0,0) * (256 - len(colours)))\r\nmcImage.putpalette(palette)\r\n<\/pre>\n<p><noscript><br \/>\nThis process gives us an image that looks like this:<\/p>\n<div id=\"attachment_1721\" style=\"width: 410px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1721\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york.png\" alt=\"OS 1:250,000 map of North East York. Contains Ordnance Survey data \u00c2\u00a9 Crown copyright and database right 2013\" width=\"200\" height=\"200\" class=\"size-full wp-image-1720\" srcset=\"https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york.png 200w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/york-150x150.png 150w\" sizes=\"auto, (max-width: 200px) 100vw, 200px\" \/><\/a> <a href=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/output.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1721\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/output.png\" alt=\"OS Map Image drawn using the 16 colour palette\" width=\"200\" height=\"200\" class=\"size-full wp-image-1721\" srcset=\"https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/output.png 200w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/output-150x150.png 150w\" sizes=\"auto, (max-width: 200px) 100vw, 200px\" \/><\/a><p id=\"caption-attachment-1721\" class=\"wp-caption-text\">Original map on left, palletised map on right<\/p><\/div>\n<h2>Bringing it all together<\/h2>\n<p>The final step is to take our quantised image and draw it inside MCPI by looping through each pixel in turn and calling MCPI&#8217;s setBlock() function with the correct position and blockID and blockData values. This is quite a slow thing to do, so it&#8217;s good that we&#8217;re only dealing with 40,000 blocks.<\/p>\n<p><script src=\"https:\/\/gist.github.com\/davstott\/6098768.js?file=blit.py\"><\/script><br \/>\n<noscript><\/p>\n<pre>\r\nmc.postToChat(\"Starting drawing\")\r\n#draw blocks at this height\r\nz = 10\r\n#if we assume the most common colour will be white, we can pre-paint the backgrund to save many setBlock()s\r\n#35, 0 is tileID for wool, tileData for white\r\nmc.setBlocks(0,z,0,WIDTH,z,HEIGHT,35,0)\r\n\r\nfor x in range(WIDTH):\r\n  for y in range(HEIGHT):\r\n    # fetch the pixel value at the current position, the units are the indexes into our palette\r\n    pix = outputImage.getpixel((x,y)) \r\n    if (pix < len(tileIdsByOffset)):\r\n      # lookup the minecraft wool tiledata ID for this palette entry\r\n      tileColour = tileIdsByOffset[pix] \r\n    else:\r\n      # didn't find one, so default to white\r\n      tileColour = 0      \r\n    if (tileColour != 0):\r\n      # only draw a block if it's not white. 35 is the block ID for wool\r\n      mc.setBlock(x, z, y, 35, tileColour)\r\nmc.postToChat(\"Finished drawing\")\r\n<\/pre>\n<p><\/noscript><\/p>\n<p>Once that's finished drawing, we can then fly our around our map inside of minecraft!<\/p>\n<div id=\"attachment_1728\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1728\" src=\"http:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955-300x225.jpg\" alt=\"Ordnance Survey map inside Minecraft Pi edition\" width=\"300\" height=\"225\" class=\"size-medium wp-image-1728\" srcset=\"https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955-300x225.jpg 300w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955-624x468.jpg 624w, https:\/\/davstott.me.uk\/wordpress\/wp-content\/uploads\/2013\/07\/smP1080955.jpg 800w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-1728\" class=\"wp-caption-text\">Ordnance Survey map inside Minecraft Pi edition<\/p><\/div>\n<h2>Code<\/h2>\n<p>As always, my code is up on <a href=\"https:\/\/github.com\/davstott\/picraft\">Github<\/a>, but the only really interesting file is the Python script <a href=\"https:\/\/github.com\/davstott\/picraft\/blob\/master\/quant.py\">quant.py<\/a>. <\/p>\n<p>Some ideas I've got for future work are to add a small amount of plumbing that would connect a webcam or picam to a MCPI 'TV' wall and to add a python WMS client that would automatically download and render more map tiles when the player gets within a certain range of the tile edge. <\/p>\n<p><iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"\/\/www.youtube.com\/embed\/zwnOW_NM4ws\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A python Ordnance Survey map viewer, drawn inside Minecraft, on a Raspberry Pi. Just what the world needs&#8230; This will also work for Minecraft drawing pretty much any small image, such as a mugshot from a webcam.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"class_list":["post-1717","post","type-post","status-publish","format-standard","hentry","category-tech"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1717","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/comments?post=1717"}],"version-history":[{"count":17,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1717\/revisions"}],"predecessor-version":[{"id":1796,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/posts\/1717\/revisions\/1796"}],"wp:attachment":[{"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/media?parent=1717"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/categories?post=1717"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davstott.me.uk\/index.php\/wp-json\/wp\/v2\/tags?post=1717"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}