souplesse

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

TileMap.elm (3825B)


      1 module TileMap exposing (tiles
      2                         , ZoomLevel(..)
      3                         , Coord
      4                         , TileMap(..)
      5                         , toCoord
      6                         , toZoom
      7                         , translate
      8                         , translatePixels
      9                         , incZoom
     10                         , bounds
     11                         , pixelsToCoord
     12                         , pixelFromCoord
     13                         , zoomStep)
     14 
     15 import Html exposing (img, div)
     16 import Html.Attributes as H exposing (src, style, width, height)
     17 
     18 import Pos exposing (Pos)
     19 
     20 type alias TileNumber = { x: Int, y: Int }
     21 
     22 -- Coordinates in a Mercator projection
     23 type alias Coord = { x: Float, y: Float }
     24 
     25 -- zoom level
     26 type alias TileZoomLevel = Int
     27 type ZoomLevel = ZoomLevel Int
     28 
     29 
     30 type TileMap = TileMap Coord ZoomLevel Int Int
     31 
     32 zoomStep = 8
     33 
     34 toZoom : ZoomLevel -> TileZoomLevel
     35 toZoom (ZoomLevel f) = f // zoomStep
     36 
     37 
     38 incZoom : ZoomLevel -> Int -> ZoomLevel
     39 incZoom (ZoomLevel z) delta =
     40     ZoomLevel (clamp 0 (20 * zoomStep) (z + delta))
     41 
     42 
     43 -- project lat/long to co-ordinates based on pseudocode at
     44 -- https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels
     45 
     46 sec x = 1 / (cos x)
     47 
     48 toCoord : Pos -> Coord
     49 toCoord pos =
     50     let
     51         lat_rad = pos.lat * pi / 180
     52         x = (pos.lon + 180) / 360
     53         y = (1 - (logBase e ((tan lat_rad) + (sec lat_rad))) / pi) / 2
     54     in
     55         Coord x y
     56 
     57 pixelsToCoord z_ (x,y) =
     58     let scale = 8 + toZoom z_
     59         x_float = toFloat x / toFloat ( 2 ^ scale)
     60         y_float = toFloat y / toFloat ( 2 ^ scale)
     61     in Coord x_float y_float
     62 
     63 reflect : Coord -> Coord
     64 reflect c = Coord -c.x -c.y
     65 
     66 -- used for Coords and for TileNumbers
     67 translate base offset =
     68     { x = (base.x + offset.x), y = (base.y + offset.y) }
     69 
     70 translatePixels : Coord -> ZoomLevel -> (Int, Int) -> Coord
     71 translatePixels old z_ (x, y) =
     72     translate old (pixelsToCoord z_ (x, y))
     73 
     74 tileCovering : Coord -> TileZoomLevel -> TileNumber
     75 tileCovering c z =
     76     TileNumber (truncate (toFloat (2 ^ z) * c.x)) (truncate (toFloat (2 ^ z) * c.y))
     77 
     78 pixelFromCoord : Coord -> ZoomLevel -> (Int, Int)
     79 pixelFromCoord c z_ =
     80     let z = toZoom z_
     81         {x,y} = tileCovering c (z + 8)
     82     in (x,y)
     83 
     84 boundingTiles : TileMap -> (TileNumber, TileNumber)
     85 boundingTiles (TileMap centre z1 width height) =
     86     -- find the tiles needed to cover the area (`width` x `height`)
     87     -- about the point at `centre`
     88     let z = toZoom z1
     89         delta = pixelsToCoord z1 ((width // 2), (height // 2))
     90         minCoord = translate centre (reflect delta)
     91         maxCoord = translate centre delta
     92     in ((tileCovering minCoord z),
     93         (translate (tileCovering maxCoord z) (TileNumber 1 1)))
     94 
     95 bounds : TileMap -> { left : Int, top : Int, width: Int, height: Int }
     96 bounds tmap =
     97     let (mintile, maxtile) = boundingTiles tmap
     98     in {
     99         left = mintile.x * 256,
    100         top = mintile.y * 256,
    101         width = (1 + maxtile.x - mintile.x) * 256,
    102         height = (1 + maxtile.y - mintile.y) * 256
    103     }
    104 
    105 
    106 tileUrl : TileNumber -> TileZoomLevel -> String
    107 tileUrl {x,y} z =
    108     String.concat ["https://a.tile.openstreetmap.org",
    109                        "/", String.fromInt z,
    110                        "/", String.fromInt x,
    111                        "/", String.fromInt y,
    112                        ".png" ]
    113 
    114 tileImg zoom tilenumber = img [ width 256,
    115                                 height 256,
    116                                 src (tileUrl tilenumber zoom) ] []
    117 
    118 tiles tmap =
    119     let (TileMap centre zoom width height) = tmap
    120         (mintile, maxtile) = boundingTiles tmap
    121         xs = List.range mintile.x maxtile.x
    122         ys = List.range mintile.y maxtile.y
    123         zoom_ = toZoom zoom
    124     in
    125     List.map
    126         (\ y -> div []
    127              (List.map (\ x -> tileImg zoom_ (TileNumber x y)) xs))
    128         ys