{"id":2371,"date":"2015-03-14T04:13:29","date_gmt":"2015-03-14T04:13:29","guid":{"rendered":"http:\/\/41j.com\/blog\/?p=2371"},"modified":"2015-03-14T04:18:18","modified_gmt":"2015-03-14T04:18:18","slug":"fractal-slippy-map-in-golang-and-leafletjs","status":"publish","type":"post","link":"https:\/\/41j.com\/blog\/2015\/03\/fractal-slippy-map-in-golang-and-leafletjs\/","title":{"rendered":"Fractal Slippy Map in golang and LeafletJS"},"content":{"rendered":"<p><a href=\"http:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/03\/fractalmap.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/03\/fractalmap.png\" alt=\"fractalmap\" width=\"480\" height=\"336\" class=\"aligncenter size-full wp-image-2373\" srcset=\"https:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/03\/fractalmap.png 480w, https:\/\/41j.com\/blog\/wp-content\/uploads\/2015\/03\/fractalmap-300x210.png 300w\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" \/><\/a><\/p>\n<p>Recently I&#8217;ve been playing with golang and LeafletJS. As a small project I decided to knock together a fractal slippy map with a golang based map server which dynamically creates the fractal tile images. The whole thing can be reverse proxied via nginx to take the load off the server. The projects totals less than 200 lines of golang as JS and these are my design notes. You can see the final project <a href=\"http:\/\/gu.pe\/map\/map.html\">here<\/a> and grab the code on github <a href=\"https:\/\/github.com\/new299\/fractalmap\">here<\/a>.<\/p>\n<h2>The HTML<\/h2>\n<p>The HTML is extremely simple, the map is a single div:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;div id=&quot;map&quot; style=&quot;width: 100px; height: 100px; display: inline-block;&quot;&gt;&lt;\/div&gt;\r\n<\/pre>\n<p>Which is then populated by LeafletJS as follows:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&lt;script src=&quot;leaflet.js&quot;&gt;&lt;\/script&gt;\r\n&lt;script&gt;\r\n  document.getElementById('map').style.width  = window.innerWidth-321 + &quot;px&quot;;\r\n  document.getElementById('map').style.height = window.innerHeight-20 + &quot;px&quot;;\r\n  \r\n  var map = L.map('map').setView(&#x5B;-10, -360], 2);\r\n\r\n  L.tileLayer('http:\/\/gu.pe\/map\/map\/{z}\/{x}\/{y}.png', {\r\n    maxZoom: 18,\r\n    attribution: 'gu.pe',\r\n    id: 'example'\r\n  }).addTo(map);\r\n&lt;\/script&gt;\r\n<\/pre>\n<p>The first couple of lines ensure that the map is sized to fill the whole window. The rest just initialises LeafletJS and tells it where to fetch map files from. Map tiles are just simple 256&#215;256 png images.<\/p>\n<h2>The golang server<\/h2>\n<p>Both the static html and the dynamically generated tiles are served via golang. Here&#8217;s the main function that initializes everything:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nfunc main() {\r\n  http.HandleFunc(&quot;\/map\/&quot;, handler)\r\n  http.Handle(&quot;\/&quot;, httpgzip.NewHandler(http.FileServer(http.Dir(&quot;.&quot;))))\r\n  http.ListenAndServe(&quot;:8080&quot;, nil)\r\n}\r\n<\/pre>\n<p>I&#8217;m using the <a href=\"http:\/\/github.com\/daaku\/go.httpgzip\">httpgzip<\/a> package which allows golang to server serve gzip compressed content. This wraps the standard golang file server on line 3 above. A hander is registered on \/map\/ and this is where the png tile images are generated.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nfunc handler(w http.ResponseWriter, r *http.Request) {\r\n\r\n  \/\/ URLs here look like http:\/\/localhost:8080\/map\/13\/4100\/2724.png\r\n  \/\/                                               z  x    y\r\n\r\n  tile_size   := 256\r\n  tile_size_f := float64(256)\r\n\r\n  \/\/ splits out the URL to get the x,y,z coordinates\r\n  spliturl := strings.Split(r.URL.Path, &quot;\/&quot;)\r\n  tile_zi, _ := strconv.Atoi(spliturl&#x5B;2])\r\n  tile_z := float64(tile_zi)\r\n  tile_xi, _ := strconv.Atoi(spliturl&#x5B;3])\r\n  tile_x := float64(tile_xi)-1\r\n  tile_yi, _ := strconv.Atoi(strings.Split(spliturl&#x5B;4],&quot;.&quot;)&#x5B;0])\r\n  tile_y := float64(tile_yi)-1\r\n  fmt.Printf(&quot;Input: %f %f %f\\n&quot;, tile_z,tile_x,tile_y)\r\n\r\n  w.Header().Set(&quot;Content-Type&quot;,&quot;image\/png&quot;)\r\n\r\n  myimage := image.NewRGBA(image.Rectangle{image.Point{0,0},image.Point{tile_size,tile_size}})\r\n\r\n  \/\/ This loop just fills the image tile with fractal data\r\n  for cx := 0; cx &lt; tile_size; cx++ {\r\n    for cy := 0; cy &lt; tile_size; cy++ {\r\n\r\n      cx_f := float64(cx)\r\n      cy_f := float64(cy)\r\n\r\n      i := complex128(complex(0,1))\r\n\r\n      zoom := float64(math.Pow(2,float64(tile_z-2)))\r\n\r\n      tile_range   := 1\/zoom\r\n      tile_start_x := 1\/zoom + (tile_range*tile_x)\r\n      tile_start_y := 1\/zoom + (tile_range*tile_y)\r\n\r\n      x := -2 + tile_start_x + (cx_f\/tile_size_f)*tile_range\r\n      y := -2 + tile_start_y + (cy_f\/tile_size_f)*tile_range\r\n\r\n      \/\/ x and y are now in the range ~-2 -&gt; +2\r\n\r\n      z := complex128(complex(x,0)) + complex128(complex(y,0))*complex128(i)\r\n\r\n      c := complex(0.274,0.008)\r\n      for n := 0; n &lt; 100; n++ {\r\n        z = z*z + complex128(c)\r\n      }\r\n\r\n      z = z *10\r\n      ratio := float64(2 * (real(z)\/2))\r\n      r     := math.Max(0, float64(255*(ratio - 1)))\r\n      b     := math.Max(0, float64(255*(1 - ratio)))\r\n      g     := float64(255 - b - r)\r\n      col := color.RGBA{uint8(r),uint8(g),uint8(b),255}\r\n      myimage.Set(cx,cy,col)\r\n    }\r\n  }\r\n\r\n  png.Encode(w, myimage)\r\n}\r\n<\/pre>\n<p>The bulk of the code is related to the Julia set generation. PNGs are relatively easy to generate and serve in golang, and I previously wrote up my notes on doing that <a href=\"http:\/\/41j.com\/blog\/2015\/03\/serving-server-generated-pngs-over-http-in-golang\/\">here<\/a>. The code that splits out the URL is also a mess, I guess something like sscanf would have made a better job of this? However I&#8217;m not sure what the canonical way of doing this in golang is.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I&#8217;ve been playing with golang and LeafletJS. As a small project I decided to knock together a fractal slippy map with a golang based map server which dynamically creates the fractal tile images. The whole thing can be reverse proxied via nginx to take the load off the server. The projects totals less than [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-2371","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1RRoU-Cf","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/2371","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/comments?post=2371"}],"version-history":[{"count":2,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/2371\/revisions"}],"predecessor-version":[{"id":2377,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/2371\/revisions\/2377"}],"wp:attachment":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/media?parent=2371"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/categories?post=2371"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/tags?post=2371"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}