simpler-tiles 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE +19 -0
  4. data/README +22 -0
  5. data/Rakefile +71 -0
  6. data/doc/SimplerTiles/Bounds.html +506 -0
  7. data/doc/SimplerTiles/Layer.html +593 -0
  8. data/doc/SimplerTiles/Map.html +2081 -0
  9. data/doc/SimplerTiles/PP.html +204 -0
  10. data/doc/SimplerTiles/Query.html +521 -0
  11. data/doc/SimplerTiles/Style.html +577 -0
  12. data/doc/SimplerTiles.html +167 -0
  13. data/doc/_index.html +188 -0
  14. data/doc/class_list.html +47 -0
  15. data/doc/css/common.css +1 -0
  16. data/doc/css/full_list.css +55 -0
  17. data/doc/css/style.css +322 -0
  18. data/doc/file.README.html +89 -0
  19. data/doc/file_list.html +49 -0
  20. data/doc/frames.html +13 -0
  21. data/doc/index.html +89 -0
  22. data/doc/js/app.js +205 -0
  23. data/doc/js/full_list.js +173 -0
  24. data/doc/js/jquery.js +16 -0
  25. data/doc/method_list.html +390 -0
  26. data/doc/top-level-namespace.html +105 -0
  27. data/ext/simpler_tiles/bounds.c +90 -0
  28. data/ext/simpler_tiles/bounds.h +17 -0
  29. data/ext/simpler_tiles/depend +7 -0
  30. data/ext/simpler_tiles/extconf.rb +42 -0
  31. data/ext/simpler_tiles/layer.c +93 -0
  32. data/ext/simpler_tiles/layer.h +16 -0
  33. data/ext/simpler_tiles/map.c +338 -0
  34. data/ext/simpler_tiles/map.h +17 -0
  35. data/ext/simpler_tiles/query.c +87 -0
  36. data/ext/simpler_tiles/query.h +17 -0
  37. data/ext/simpler_tiles/simpler_tiles.c +16 -0
  38. data/ext/simpler_tiles/simpler_tiles.h +25 -0
  39. data/ext/simpler_tiles/style.c +106 -0
  40. data/ext/simpler_tiles/style.h +17 -0
  41. data/index.erb +459 -0
  42. data/index.html +439 -0
  43. data/lib/simpler_tiles/bounds.rb +19 -0
  44. data/lib/simpler_tiles/layer.rb +25 -0
  45. data/lib/simpler_tiles/map.rb +55 -0
  46. data/lib/simpler_tiles/mixins/pp.rb +13 -0
  47. data/lib/simpler_tiles/query.rb +28 -0
  48. data/lib/simpler_tiles/style.rb +19 -0
  49. data/lib/simpler_tiles/version.rb +4 -0
  50. data/lib/simpler_tiles.rb +13 -0
  51. data/simpler-tiles-logo.png +0 -0
  52. data/simpler-tiles.gemspec +30 -0
  53. data/test/helper.rb +8 -0
  54. data/test/test_map.rb +67 -0
  55. data/test/test_simpler_tiles.rb +26 -0
  56. metadata +199 -0
@@ -0,0 +1,17 @@
1
+ #ifndef _SIMPLER_STYLE_H
2
+ #define _SIMPLER_STYLE_H
3
+
4
+ #include "simpler_tiles.h"
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ void init_style();
11
+ extern VALUE cSimplerTilesStyle;
12
+
13
+ #ifdef __cplusplus
14
+ }
15
+ #endif
16
+
17
+ #endif
data/index.erb ADDED
@@ -0,0 +1,459 @@
1
+ <%
2
+ def highlight(lang, code)
3
+ IO.popen("pygmentize -f html -l #{lang}", 'w+') do |p|
4
+ p.puts code
5
+ p.close_write
6
+ p.read
7
+ end
8
+ end
9
+ %>
10
+ <!doctype html>
11
+ <html>
12
+ <head>
13
+ <title>Simpler Tiles</title>
14
+ <!--
15
+ +-----------------------------+
16
+ | / ~|
17
+ | ---------- /|
18
+ | Simpler / --------- |
19
+ | / ~ / ** |
20
+ | / / **** |
21
+ | ---- ~ / ****** |
22
+ | / ~~ -- ******* |
23
+ | / / ***** |
24
+ | / / |
25
+ | / ~~ / Tiles |
26
+ |- / |
27
+ | ~~~ / |
28
+ +-----------------------------+
29
+ -->
30
+ <meta charset="utf-8">
31
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
32
+ <style>
33
+ /* reset */
34
+ div, html, body {
35
+ margin: 0;
36
+ padding: 0;
37
+ border: 0;
38
+ vertical-align: baseline;
39
+ }
40
+
41
+ ul { list-style: none; padding-left: 10px;}
42
+ li { margin-bottom: 1em; }
43
+ /* text styles */
44
+ body {
45
+ font-family: "Helvetica Nueue", Helvetica, sans-serif;
46
+ font-size: 14px;
47
+ line-height: 1.7em;
48
+ margin-left: auto;
49
+ margin-right: auto;
50
+ width: 600px;
51
+ padding: 20px;
52
+ }
53
+ p, li {
54
+ width: 600px;
55
+ margin: 0px 0px 1em;
56
+ }
57
+
58
+ h1, h2, h3 {
59
+ text-rendering: optimizeLegibility;
60
+ margin-left: -5px;
61
+ }
62
+
63
+ h4 {
64
+ margin: 0px;
65
+ margin-top: 30px;
66
+ margin-left: -5px;
67
+ font-weight: normal;
68
+ }
69
+
70
+ h4 code {
71
+ padding: 4px;
72
+ background-color: #e6f3ff;
73
+ }
74
+
75
+ ol {
76
+ padding-left: 0px;
77
+ }
78
+
79
+ code, pre, tt { font-family: Monaco, monospace; font-size: 12px; }
80
+ tt { border:1px solid #efefef; padding: 2px;}
81
+ dd { margin-left: 1em; }
82
+ a { color: black; }
83
+ a:hover { text-decoration: none; }
84
+ pre {
85
+ padding-left: 10px;
86
+ font-size: 12px;
87
+ border-left: 5px solid #efefef;
88
+ line-height: 1.3;
89
+ }
90
+
91
+ hr {
92
+ border: 0;
93
+ border-top: 1px solid #efefef;
94
+ height: 1px;
95
+ }
96
+
97
+ /* styles stolen from docco */
98
+ body .hll { background-color: #ffffcc }
99
+ body .c { color: #408080; font-style: italic } /* Comment */
100
+ body .err { border: 1px solid #FF0000 } /* Error */
101
+ body .k { color: #954121 } /* Keyword */
102
+ body .o { color: #666666 } /* Operator */
103
+ body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
104
+ body .cp { color: #BC7A00 } /* Comment.Preproc */
105
+ body .c1 { color: #408080; font-style: italic } /* Comment.Single */
106
+ body .cs { color: #408080; font-style: italic } /* Comment.Special */
107
+ body .gd { color: #A00000 } /* Generic.Deleted */
108
+ body .ge { font-style: italic } /* Generic.Emph */
109
+ body .gr { color: #FF0000 } /* Generic.Error */
110
+ body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
111
+ body .gi { color: #00A000 } /* Generic.Inserted */
112
+ body .go { color: #808080 } /* Generic.Output */
113
+ body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
114
+ body .gs { font-weight: bold } /* Generic.Strong */
115
+ body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
116
+ body .gt { color: #0040D0 } /* Generic.Traceback */
117
+ body .kc { color: #954121 } /* Keyword.Constant */
118
+ body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
119
+ body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
120
+ body .kp { color: #954121 } /* Keyword.Pseudo */
121
+ body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
122
+ body .kt { color: #B00040 } /* Keyword.Type */
123
+ body .m { color: #666666 } /* Literal.Number */
124
+ body .s { color: #219161 } /* Literal.String */
125
+ body .na { color: #7D9029 } /* Name.Attribute */
126
+ body .nb { color: #954121 } /* Name.Builtin */
127
+ body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
128
+ body .no { color: #880000 } /* Name.Constant */
129
+ body .nd { color: #AA22FF } /* Name.Decorator */
130
+ body .ni { color: #999999; font-weight: bold } /* Name.Entity */
131
+ body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
132
+ body .nf { color: #0000FF } /* Name.Function */
133
+ body .nl { color: #A0A000 } /* Name.Label */
134
+ body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
135
+ body .nt { color: #954121; font-weight: bold } /* Name.Tag */
136
+ body .nv { color: #19469D } /* Name.Variable */
137
+ body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
138
+ body .w { color: #bbbbbb } /* Text.Whitespace */
139
+ body .mf { color: #666666 } /* Literal.Number.Float */
140
+ body .mh { color: #666666 } /* Literal.Number.Hex */
141
+ body .mi { color: #666666 } /* Literal.Number.Integer */
142
+ body .mo { color: #666666 } /* Literal.Number.Oct */
143
+ body .sb { color: #219161 } /* Literal.String.Backtick */
144
+ body .sc { color: #219161 } /* Literal.String.Char */
145
+ body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
146
+ body .s2 { color: #219161 } /* Literal.String.Double */
147
+ body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
148
+ body .sh { color: #219161 } /* Literal.String.Heredoc */
149
+ body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
150
+ body .sx { color: #954121 } /* Literal.String.Other */
151
+ body .sr { color: #BB6688 } /* Literal.String.Regex */
152
+ body .s1 { color: #219161 } /* Literal.String.Single */
153
+ body .ss { color: #19469D } /* Literal.String.Symbol */
154
+ body .bp { color: #954121 } /* Name.Builtin.Pseudo */
155
+ body .vc { color: #19469D } /* Name.Variable.Class */
156
+ body .vg { color: #19469D } /* Name.Variable.Global */
157
+ body .vi { color: #19469D } /* Name.Variable.Instance */
158
+ body .il { color: #666666 } /* Literal.Number.Integer.Long */
159
+ </style>
160
+ </head>
161
+ <body>
162
+ <img src="simpler-tiles-logo.png">
163
+ <p>
164
+ <a href="http://github.com/propublica/simpler-tiles">Simpler Tiles</a>
165
+ is a library of ruby bindings for
166
+ <a href="http://propublica.github.com/simple-tiles/">Simple Tiles</a>, a
167
+ GIS image generation library. It allows you to generate PNG based map
168
+ images without having to dip into straight C and easily connects with
169
+ ActiveRecord. It can display any vector data that
170
+ <a href="http://www.gdal.org/ogr/ogr_formats.html">OGR</a> can read.
171
+ </p>
172
+
173
+ <p>
174
+ The source is at <a href="http://github.com/propublica/simpler-tiles">Github</a>.
175
+ You can install the gem with:
176
+ <pre>
177
+ $ gem install simpler-tiles
178
+ </pre>
179
+ </p>
180
+
181
+ <p>
182
+ The api docs are <a href="doc/index.html">here</a>, and if you have
183
+ comments or questions head on over to <tt>#newsapps</tt> or <tt>#propublica</tt>
184
+ on Freenode, or post them in the github issue
185
+ <a href="https://github.com/propublica/simpler-tiles/issues?sort=created&direction=desc&state=open">tracker</a>.
186
+ </p>
187
+
188
+ <h2>Dependencies</h2>
189
+
190
+ <p>
191
+ Simpler Tiles depends on <a href="http://propublica.github.com/simple-tiles/">Simple Tiles</a>, <a href="http://www.gdal.org/ogr/">OGR</a>, <a href="http://cairographics.org/">Cairo</a> and <a href="http://www.pango.org/">Pango</a>. You can find
192
+ installation instructions for these libraries in the
193
+ <a href="http://propublica.github.com/simple-tiles/#dependencies">Simple Tiles
194
+ documentation</a>.
195
+ </p>
196
+
197
+ <h2>Overview</h2>
198
+
199
+ <p>
200
+ Simpler Tiles contains a hierarchy of objects:
201
+ </p>
202
+ <ul>
203
+ <li>
204
+ <tt><a href="doc/SimplerTiles/Map.html">SimplerTiles::Map</a></tt>: wraps
205
+ the <tt><a href="http://propublica.github.com/simple-tiles/#maps">simplet_map_t</a></tt>
206
+ C structure and contains projection information, <tt>SimplerTiles::Bounds</tt>
207
+ and a list of <tt>SimplerTiles::Layer</tt> objects. If you are using ActiveRecord
208
+ you can connect to and query your database backend by calling the
209
+ <tt>ar_layer</tt> method.
210
+ </li>
211
+ <li>
212
+ <tt><a href="doc/SimplerTiles/Bounds.html">SimplerTiles::Bounds</a></tt>: wraps
213
+ the <tt><a href="http://propublica.github.com/simple-tiles/#bounds">simplet_bounds_t</a></tt>
214
+ C structure and specifies the limit of the map in spatial coordinates.
215
+ </li>
216
+ <li>
217
+ <tt><a href="doc/SimplerTiles/Map.html">SimplerTiles::Layer</a></tt>: wraps
218
+ the <tt><a href="http://propublica.github.com/simple-tiles/#layers">simplet_layer_t</a></tt>
219
+ C structure and contains an OGR connection string for a particular data source,
220
+ and contains a list of <tt>SimplerTiles::Query</tt> objects.
221
+ </li>
222
+ <li>
223
+ <tt><a href="doc/SimplerTiles/Query.html">SimplerTiles::Query</a></tt>: wraps
224
+ the <tt><a href="http://propublica.github.com/simple-tiles/#queries">simplet_query_t</a></tt>
225
+ C structure and contains an OGR SQL string to filter its parent layer's
226
+ data source.
227
+ </li>
228
+ <li>
229
+ <tt><a href="doc/SimplerTiles/Style.html">SimplerTiles::Style</a></tt>: wraps
230
+ the <tt><a href="http://propublica.github.com/simple-tiles/#style">simplet_style_t</a></tt>
231
+ C structure and specifies a Simple Tiles
232
+ <a href="http://propublica.github.com/simple-tiles/#styles">style declaration</a>.
233
+ </li>
234
+ </ul>
235
+
236
+ <p>
237
+ As an example of how this fits together, here is a script that generates
238
+ the Simpler Tiles logo (full repository
239
+ <a href="https://github.com/thejefflarson/simpler-tiles-contours">here</a>):
240
+ </p>
241
+
242
+ <%= highlight 'ruby', <<-CODE
243
+
244
+ require 'rubygems'
245
+ require 'simpler_tiles'
246
+
247
+ ROOT = File.expand_path(File.dirname(__FILE__))
248
+
249
+ map = SimplerTiles::Map.new do |m|
250
+ m.srs = "EPSG:3083"
251
+ m.bgcolor = "#ffffff"
252
+ m.width = 423
253
+ m.height = 260
254
+ m.set_bounds(-585080.885134, 6849466.721081, 4161303.603672, 9587780.816356)
255
+
256
+ Dir["\#\{ROOT\}/data/*.shp"].each do |shp|
257
+ m.layer shp do |l|
258
+ l.query "select * from '\#\{File.basename shp, '.shp'\}'" do |q|
259
+ q.styles "stroke" => "#111111",
260
+ "line-join" => "round",
261
+ "weight" => "0.1"
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ File.open("\#\{ROOT\}/out.png", 'w') {|f| f.write map.to_png }
268
+
269
+ CODE
270
+ %>
271
+
272
+
273
+ <h2>A Basic Tile Server</h2>
274
+
275
+ <p>
276
+ Simpler Tiles was designed to make <a href="http://wiki.openstreetmap.org/wiki/Slippy_Map">slippy maps</a>. The following is a basic tile
277
+ server using <a href="http://sinatrarb.com">Sinatra</a>. It will open display any shapefile on your hard drive
278
+ by visiting a url like: <a href="">http://localhost:4567/shape/path/to/shapefile.shp</a>
279
+ </p>
280
+
281
+ <%= highlight 'ruby', <<-CODE
282
+
283
+ require 'rubygems'
284
+ require 'sinatra'
285
+ require 'simpler_tiles'
286
+
287
+ # Grab the user's home directory
288
+ ROOT = File.expand_path("~")
289
+
290
+ # Set up a route that will grab the path to a shapefile and render the
291
+ # index template below.
292
+ get '/shape/*' do
293
+ erb :index
294
+ end
295
+
296
+ # Set up the tile url to capture x, y, z coordinates for slippy tile generation
297
+ get '/tiles/*/:x/:y/:z.png' do
298
+
299
+ # Let the browser know we are sending a png
300
+ content_type 'image/png'
301
+
302
+ # Create a Map object
303
+ map = SimplerTiles::Map.new do |m|
304
+ # Set the background color to black
305
+ m.bgcolor = "#000000"
306
+
307
+ # Set the slippy map parameters from the url
308
+ m.slippy params[:x].to_i, params[:y].to_i, params[:z].to_i
309
+
310
+ # Add a layer based on the parameters in the URL
311
+ m.layer File.join(ROOT, params[:splat].first) do |l|
312
+
313
+ # Grab all of the data from the shapefile
314
+ l.query "select * from '\#\{File.basename(params[:splat].first, '.shp')\}'" do |q|
315
+
316
+ # Add a style for stroke, fill, weight and set the line-join to be round
317
+ q.styles 'stroke' => '#002240',
318
+ 'weight' => '1',
319
+ 'line-join' => 'round',
320
+ 'fill' => '#ffffff'
321
+ end
322
+ end
323
+ end
324
+
325
+ # Finally, render the map and ship it off
326
+ map.to_png
327
+ end
328
+
329
+ # A simple inline template for the map
330
+ __END__
331
+
332
+ @@index
333
+ <!doctype html>
334
+ <html>
335
+ <head>
336
+ <script src="http://leaflet.cloudmade.com/dist/leaflet.js"></script>
337
+ <link rel="stylesheet" href="http://leaflet.cloudmade.com/dist/leaflet.css">
338
+ <style>
339
+ body, html {
340
+ margin: 0;
341
+ padding: 0;
342
+ background-color: #000000;
343
+ width: 100%;
344
+ height: 100%;
345
+ }
346
+ #map {
347
+ width: 100%;
348
+ height: 100%;
349
+ }
350
+ </style>
351
+ </head>
352
+ <body>
353
+ <div id="map"></div>
354
+ <script>
355
+ var map = new L.Map('map');
356
+ var layer = new L.TileLayer('/tiles/\<\%\= params[:splat] \%\>/{x}/{y}/{z}.png')
357
+ map.addLayer(layer).setView(new L.LatLng(38, -95), 1);
358
+ </script>
359
+ </body>
360
+ </html>
361
+
362
+ CODE
363
+ %>
364
+
365
+ <p>
366
+ <strong>That's in no way a safe or secure thing to put on the internet</strong>, but it will
367
+ do as a simple replacement for QGIS!
368
+ </p>
369
+
370
+ <h2>Tips and Tricks</h2>
371
+
372
+ <p>
373
+ Deploying dynamically generated maps is difficult because of the data sizes and
374
+ processing required to render them. At ProPublica we have been following
375
+ these best practices:
376
+ </p>
377
+
378
+ <ol>
379
+ <li>
380
+ If you are using a PostGIS backend make sure to limit your queries to
381
+ the bounds of the image. You will want to add a <tt>where</tt> clause to each
382
+ trip to the database like so:
383
+ <%= highlight 'ruby', <<-CODE
384
+
385
+ layer.query Shape.where("the_geom && SRID=4326;\#\{map.buffered_bounds.reproject(map.srs, 'epsg:4326').to_wkt}'\}").to_sql do |query|
386
+ # ...
387
+ end
388
+
389
+ CODE
390
+ %>
391
+ This will protect against doing a full table scan in your database.
392
+ </li>
393
+ <li>
394
+ <p>
395
+ If you are serving images as part of a tiled map, you'll also want to
396
+ put a proxy in front of your server with far future expiration date. At
397
+ ProPublica we're using cloudfront to serve this tile:
398
+ </p>
399
+
400
+ <img src="http://tiles-a.propublica.org/redistricting-maps/tiles/CA/12/330/789/11.png">
401
+ <p>
402
+ It will also speed up browser-side rendering if you have multiple domains
403
+ that proxy the tiles (eg. tiles-a.propublica.org, tiles-b.propublica.org, etc.)
404
+ </p>
405
+ </li>
406
+ <li>
407
+ Because of the extra time spent in collision detection for labels, you'll
408
+ probably want to limit the amount of labels you are displaying on a particular
409
+ tile.
410
+ </li>
411
+ <li>
412
+ If you are displaying labels (cf.
413
+ <a href="http://propublica.github.com/simple-tiles/#styles">text-field and font styles</a>)
414
+ you'll want to use a buffer -- usually a quarter to a half of a tile
415
+ (i.e. for a 256 tile set the buffer to be 64 to 128 pixels) -- so that
416
+ the layout isn't clipped at tile boundaries. You can assign a buffer on
417
+ <tt>SimplerTiles::Map</tt> objects like so:
418
+
419
+ <%= highlight 'ruby', <<-CODE
420
+
421
+ map.buffer 128
422
+
423
+ CODE
424
+ %>
425
+ </li>
426
+ <li>
427
+ Finally, for truly speedy maps, storing your spatial data in the Web Mercator
428
+ projection -- <tt>epsg:3785</tt> -- will cut down on response time
429
+ because each geometry object won't need to be reprojected.
430
+ </li>
431
+ </ol>
432
+
433
+ <h2>License</h2>
434
+
435
+ <pre>
436
+ Copyright (c) 2012, ProPublica
437
+
438
+ Permission is hereby granted, free of charge, to any person obtaining a copy
439
+ of this software and associated documentation files (the "Software"), to deal
440
+ in the Software without restriction, including without limitation the rights
441
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
442
+ copies of the Software, and to permit persons to whom the Software is furnished
443
+ to do so, subject to the following conditions:
444
+
445
+ The above copyright notice and this permission notice shall be included in all
446
+ copies or substantial portions of the Software.
447
+
448
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
449
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
450
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
451
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
452
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
453
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
454
+ IN THE SOFTWARE.
455
+ </pre>
456
+
457
+ <p><em>Simpler Tiles is a project of ProPublica.</em></p>
458
+ </body>
459
+ </html>