mapplz 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7034405767e5ef4ee3c72c162821971aaa06b27c
4
+ data.tar.gz: 3cb3eec174363117097f84b9ea8ed093ffb49255
5
+ SHA512:
6
+ metadata.gz: 19aa227e8159c73ec9fbc3c63519b2613fd0829aecae4a891d48df93309bd0e9a042b44177934713e86eddf6fdd7d4c418afb059e7672e69769c1fe95e111663
7
+ data.tar.gz: 443b860f75737c92837f1f9ef4a1d5121670470f002229f884e1e9be337dcb319966aeb3c5c4b4a8947324d7770966635c8d670ec72460f7c5260ca309d3b076
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # MapPLZ-Ruby
2
+
3
+ [MapPLZ](http://mapplz.com) is a framework to make mapping quick and easy in
4
+ your favorite language.
5
+
6
+ ## Getting started
7
+
8
+ Extract, transform, and load geodata into MapPLZ:
9
+
10
+ ```
11
+ mapstore = MapPLZ.new
12
+
13
+ # a point
14
+ mapstore << [lat, lng]
15
+ mapstore.add( [lat, lng] )
16
+
17
+ # multiple points
18
+ mapstore << [point1, point2]
19
+
20
+ # a line or shape
21
+ mapstore << [[point1, point2, point3]]
22
+
23
+ # GeoJSON string or object
24
+ mapstore << { type: "Feature", geometry: { type: "Point", coordinates: [lng, lat] } }
25
+ ```
26
+
27
+ Include properties along with the geo data:
28
+
29
+ ```
30
+ # an array of attributes
31
+ pt1 = mapstore << [lat, lng, color, cost]
32
+ pt2 = mapstore << [lat, lng, color2, cost2]
33
+ # pt1.properties = [color, cost]
34
+
35
+ # a hash or JSON string of attributes
36
+ mapstore << [lat, lng, { color: 'red', cost: 10 }]
37
+
38
+ # GeoJSON properties
39
+ mapstore << { type: "Feature", geometry: { type: "Point", properties: { name: "Bella" }, coordinates: [lng, lat] } }
40
+ ```
41
+
42
+ ## Export HTML and GeoJSON
43
+
44
+ Currently you can output the data as GeoJSON:
45
+
46
+ ```
47
+ @mapper = MapPLZ.new
48
+ @mapper << mapplz_content
49
+ @mapper.to_geojson
50
+ ```
51
+
52
+ You will be able to output an interactive, HTML+JavaScript map with Leaflet.js
53
+
54
+ ```
55
+ require 'mapplz'
56
+
57
+ @mapper = MapPLZ.new
58
+ @mapper << mapplz_code
59
+ @mapper.render_html
60
+ ```
61
+
62
+ You would be able to use it in Rails + HAML templates, too:
63
+
64
+ ```
65
+ div#map
66
+ = @mapper.embed_html
67
+ ```
68
+
69
+ ## MapPLZ queries
70
+
71
+ All of these are valid ways to query geodata:
72
+
73
+ ```
74
+ # with a value
75
+ mapplz.where('layer = ?', name_of_layer)
76
+
77
+ # get a count
78
+ mapplz.count
79
+ mapplz.count('layer = ?', name_of_layer)
80
+ ```
81
+
82
+ ## MapPLZ language
83
+ You can make a map super quickly by using the MapPLZ language. A MapPLZ map
84
+ can be described using as simply as this:
85
+
86
+ ```
87
+ map
88
+ marker
89
+ "The Statue of Liberty"
90
+ [40, -70]
91
+ plz
92
+ plz
93
+ ```
94
+
95
+ ## MapPLZ and Databases
96
+
97
+ You can store geodata in SQLite/Spatialite databases, or in Postgres/PostGIS
98
+ databases, using a simplified MapPLZ API.
99
+
100
+ ```
101
+ # working with records
102
+ pt = mapstore << [lat, lng]
103
+ pt.name = "Sears Tower"
104
+ pt.save!
105
+ ```
106
+
107
+ ### COMING SOON
108
+
109
+ ```
110
+ # near a point
111
+ mapplz.near([lat, lng])
112
+
113
+ # in an area
114
+ mapplz.inside([point1, point2, point3, point1])
115
+ ```
116
+
117
+ ## License
118
+
119
+ Free BSD License
@@ -0,0 +1,4 @@
1
+ (function($) {
2
+ $('.mapplz')
3
+
4
+ })(jQuery);
@@ -0,0 +1,12 @@
1
+ #main_map.mapplz {
2
+ width: 100%;
3
+ height: 100%;
4
+ /* prevent 0-height or 0-width div bug */
5
+ min-width: 150px;
6
+ min-height: 150px;
7
+ }
8
+
9
+ /* override bootstrap + Google Maps issue */
10
+ .mapplz img {
11
+ max-width: 100%;
12
+ }
@@ -0,0 +1 @@
1
+ #main_map.mapplz
data/lib/mapplz.rb ADDED
@@ -0,0 +1,410 @@
1
+ # Encoding: utf-8
2
+
3
+ require 'sql_parser'
4
+ require 'json'
5
+
6
+ # MapPLZ datastore
7
+ class MapPLZ
8
+ def initialize
9
+ @db_type = 'array'
10
+ @parser = SqlParser.new
11
+ @my_array = []
12
+ end
13
+
14
+ def add(user_geo)
15
+ geo_objects = standardize_geo(user_geo)
16
+ @my_array += geo_objects
17
+
18
+ if geo_objects.length == 1
19
+ geo_objects[0]
20
+ else
21
+ geo_objects
22
+ end
23
+ end
24
+
25
+ def count(where_clause = nil, add_on = nil)
26
+ results = query(where_clause, add_on)
27
+ if @db_type == 'array'
28
+ results.length
29
+ else
30
+ results.count
31
+ end
32
+ end
33
+
34
+ def query(where_clause, add_on)
35
+ if where_clause.present?
36
+ if @db_type == 'array'
37
+ query_array(where_clause, add_on)
38
+ else
39
+ # @my_db.where(where_clause, add_on)
40
+ end
41
+ else
42
+ # count all
43
+ if @db_type == 'array'
44
+ @my_array
45
+ else
46
+ # @my_db.all
47
+ end
48
+ end
49
+ end
50
+
51
+ def code(mapplz_code)
52
+ @code_lines = mapplz_code.gsub("\r", '').split("\n")
53
+ @code_level = 'toplevel'
54
+ @button_layers = []
55
+ @code_button = 0
56
+ @code_layers = []
57
+ @code_label = ''
58
+ @code_color = nil
59
+ code_line(0)
60
+ @code_layers
61
+ end
62
+
63
+ def to_geojson
64
+ feature_list = []
65
+ if @db_type == 'array'
66
+ @my_array.each do |feature|
67
+ feature_list << as_geojson(feature)
68
+ end
69
+ end
70
+ geojson = { type: 'FeatureCollection', features: feature_list }
71
+ geojson.to_json
72
+ end
73
+
74
+ # alias methods
75
+
76
+ # aliases for add
77
+ def <<(user_geo)
78
+ add(user_geo)
79
+ end
80
+
81
+ def push(user_geo)
82
+ add(user_geo)
83
+ end
84
+
85
+ # aliases for count
86
+ def size(where_clause = nil, add_on = nil)
87
+ count(where_clause, add_on)
88
+ end
89
+
90
+ def length(where_clause = nil, add_on = nil)
91
+ count(where_clause, add_on)
92
+ end
93
+
94
+ private
95
+
96
+ def code_line(index)
97
+ return if index >= @code_lines.length
98
+ line = @code_lines[index].strip
99
+ codeline = line.downcase.split(' ')
100
+
101
+ if @code_level == 'toplevel'
102
+ @code_level = 'map' if line.index('map')
103
+ return code_line(index + 1)
104
+
105
+ elsif @code_level == 'map' || @code_level == 'button'
106
+ if codeline.index('button') || codeline.index('btn')
107
+ @code_level = 'button'
108
+ @button_layers << { layers: [] }
109
+ @code_button = @button_layers.length
110
+ end
111
+
112
+ if codeline.index('marker')
113
+ @code_level = 'marker'
114
+ @code_latlngs = []
115
+ return code_line(index + 1)
116
+ elsif codeline.index('line')
117
+ @code_level = 'line'
118
+ @code_latlngs = []
119
+ return code_line(index + 1)
120
+ elsif codeline.index('shape')
121
+ @code_level = 'shape'
122
+ @code_latlngs = []
123
+ return code_line(index + 1)
124
+ end
125
+
126
+ if codeline.index('plz') || codeline.index('please')
127
+ if @code_level == 'map'
128
+ @code_level = 'toplevel'
129
+ return
130
+ elsif @code_level == 'button'
131
+ # add button
132
+ @code_level = 'map'
133
+ @code_button = nil
134
+ return code_line(index + 1)
135
+ end
136
+ end
137
+
138
+ elsif @code_level == 'marker' || @code_level == 'line' || @code_level == 'shape'
139
+ if codeline.index('plz') || codeline.index('please')
140
+
141
+ if @code_level == 'marker'
142
+ @code_layers << {
143
+ lat: @code_latlngs[0][0],
144
+ lng: @code_latlngs[0][1],
145
+ label: @code_label || ''
146
+ }
147
+ elsif @code_level == 'line'
148
+ @code_layers << {
149
+ path: @code_latlngs,
150
+ strokeColor: (@code_color || ''),
151
+ label: @code_label || ''
152
+ }
153
+ elsif @code_level == 'shape'
154
+ @code_layers << {
155
+ paths: @code_latlngs,
156
+ strokeColor: (@code_color || ''),
157
+ fillColor: (@code_color || ''),
158
+ label: @code_label || ''
159
+ }
160
+ end
161
+
162
+ if @code_button
163
+ @code_level = 'button'
164
+ else
165
+ @code_level = 'map'
166
+ end
167
+
168
+ @code_latlngs = []
169
+ return code_line(index + 1)
170
+ end
171
+
172
+ end
173
+
174
+ # geocoding starts with @
175
+
176
+ # reading a color
177
+ if codeline[0].index('#') == 0
178
+ @code_color = codeline[0]
179
+ if @code_color.length != 4 && @code_color.length != 7
180
+ # named color
181
+ @code_color = @code_color.gsub('#', '')
182
+ end
183
+
184
+ if @code_level == 'button'
185
+ # button color
186
+ end
187
+
188
+ return codeline(index + 1)
189
+ end
190
+
191
+ # reading a raw string (probably text for a popup)
192
+ if codeline[0].index('"') == 0
193
+ # check button
194
+ @code_label = line[(line.index('"') + 1)..line.length]
195
+ @code_label = @code_label[0..(@code_label.index('"') - 1)]
196
+ end
197
+
198
+ # reading a latlng coordinate
199
+ if line.index('[') && line.index(',') && line.index(']')
200
+ latlng_line = line.gsub('[', '').gsub(']', '').split(',').map! { |num| num.to_f }
201
+
202
+ # must be a 2D coordinate
203
+ return codeline(index + 1) if latlng_line.length != 2
204
+
205
+ @code_latlngs << latlng_line
206
+
207
+ return code_line(index + 1)
208
+ end
209
+
210
+ code_line(index + 1)
211
+ end
212
+
213
+ def standardize_geo(user_geo)
214
+ geo_objects = []
215
+
216
+ if user_geo.is_a?(String)
217
+ begin
218
+ user_geo = JSON.parse(user_geo)
219
+ rescue
220
+ # not JSON string - attempt mapplz parse
221
+ return code(user_geo)
222
+ end
223
+ end
224
+
225
+ if user_geo.is_a?(Array) && user_geo.length > 0
226
+ # multiple objects being added? iterate through
227
+ if user_geo[0].is_a?(Hash) || user_geo[0].is_a?(Array)
228
+ user_geo.each do |geo_piece|
229
+ geo_objects += standardize_geo(geo_piece)
230
+ end
231
+ return geo_objects
232
+ end
233
+
234
+ # first two spots are a coordinate
235
+ validate_lat = user_geo[0].to_f != 0 || user_geo[0].to_s == '0'
236
+ validate_lng = user_geo[1].to_f != 0 || user_geo[1].to_s == '0'
237
+
238
+ if validate_lat && validate_lng
239
+ geo_object = {
240
+ lat: user_geo[0].to_f,
241
+ lng: user_geo[1].to_f
242
+ }
243
+ else
244
+ fail 'no latitude or longitude found'
245
+ end
246
+
247
+ # assume user properties are an ordered array of values known to the user
248
+ user_properties = user_geo.drop(2)
249
+
250
+ # only one property and it's a hash? it's a hash of properties
251
+ if user_properties.length == 1 && user_properties[0].is_a?(Hash)
252
+ user_properties[0].keys.each do |key|
253
+ geo_object[key.to_sym] = user_properties[0][key]
254
+ end
255
+ else
256
+ geo_object[:properties] = user_properties
257
+ end
258
+
259
+ geo_objects << geo_object
260
+
261
+ elsif user_geo.is_a?(Hash)
262
+ # check for lat and lng
263
+ validate_lat = false
264
+ validate_lat = 'lat' if user_geo.key?('lat') || user_geo.key?(:lat)
265
+ validate_lat ||= 'latitude' if user_geo.key?('latitude') || user_geo.key?(:latitude)
266
+
267
+ validate_lng = false
268
+ validate_lng = 'lng' if user_geo.key?('lng') || user_geo.key?(:lng)
269
+ validate_lng ||= 'lon' if user_geo.key?('lon') || user_geo.key?(:lon)
270
+ validate_lng ||= 'long' if user_geo.key?('long') || user_geo.key?(:long)
271
+ validate_lng ||= 'longitude' if user_geo.key?('longitude') || user_geo.key?(:longitude)
272
+
273
+ if validate_lat && validate_lng
274
+ # single hash
275
+ geo_object = {
276
+ lat: user_geo[validate_lat].to_f,
277
+ lng: user_geo[validate_lng].to_f
278
+ }
279
+ user_geo.keys.each do |key|
280
+ next if key == validate_lat || key == validate_lng
281
+ geo_object[key.to_sym] = user_geo[key]
282
+ end
283
+ geo_objects << geo_object
284
+ else
285
+ # try GeoJSON
286
+ if user_geo.key?('type')
287
+ if user_geo['type'] == 'FeatureCollection' && user_geo.key?('features')
288
+ # recursive onto features
289
+ user_geo['features'].each do |feature|
290
+ geo_objects += standardize_geo(feature)
291
+ end
292
+ elsif user_geo.key?('geometry') && user_geo['geometry'].key?('coordinates')
293
+ # individual feature
294
+ geo_object = {}
295
+ coordinates = user_geo['geometry']['coordinates']
296
+ if user_geo.key?('properties')
297
+ user_geo['properties'].keys.each do |key|
298
+ geo_object[key.to_sym] = user_geo['properties'][key]
299
+ end
300
+ end
301
+
302
+ if user_geo['geometry']['type'] == 'Point'
303
+ geo_object[:lat] = coordinates[1].to_f
304
+ geo_object[:lng] = coordinates[0].to_f
305
+ end
306
+
307
+ geo_objects << geo_object
308
+ end
309
+ elsif user_geo.key?(:type)
310
+ if user_geo[:type] == 'FeatureCollection' && user_geo.key?(:features)
311
+ # recursive onto features
312
+ user_geo[:features].each do |feature|
313
+ geo_objects += standardize_geo(feature)
314
+ end
315
+ elsif user_geo.key?(:geometry) && user_geo[:geometry].key?(:coordinates)
316
+ # individual feature
317
+ geo_object = {}
318
+ coordinates = user_geo[:geometry][:coordinates]
319
+ if user_geo.key?(:properties)
320
+ user_geo[:properties].keys.each do |key|
321
+ geo_object[key.to_sym] = user_geo[:properties][key]
322
+ end
323
+ end
324
+
325
+ if user_geo[:geometry][:type] == 'Point'
326
+ geo_object[:lat] = coordinates[1].to_f
327
+ geo_object[:lng] = coordinates[0].to_f
328
+ end
329
+
330
+ geo_objects << geo_object
331
+ end
332
+ end
333
+ end
334
+ end
335
+
336
+ geo_objects
337
+ end
338
+
339
+ def as_geojson(geo_object)
340
+ if geo_object.key?(:properties)
341
+ property_list = geo_object[:properties]
342
+ else
343
+ property_list = geo_object.clone
344
+ property_list.delete(:lat)
345
+ property_list.delete(:lng)
346
+ property_list.delete(:path)
347
+ end
348
+
349
+ output_geo = {
350
+ type: 'Feature',
351
+ properties: property_list
352
+ }
353
+
354
+ if geo_object.key?(:lat) && geo_object.key?(:lng)
355
+ # point
356
+ output_geo[:geometry] = {
357
+ type: 'Point',
358
+ coordinates: [geo_object[:lng], geo_object[:lat]]
359
+ }
360
+ else
361
+ # other geometry
362
+ output_geo[:geometry] = {
363
+ type: 'Polyline',
364
+ coordinates: [geo_object[:path]]
365
+ }
366
+ end
367
+ output_geo
368
+ end
369
+
370
+ def query_array(where_clause, add_on = nil)
371
+ # prepare where clause for parse
372
+ where_clause.downcase! unless where_clause.blank?
373
+ where_clause = where_clause.gsub('?', '\'?\'') if add_on.present?
374
+ where_clause = 'select * from bogus_table where ' + where_clause
375
+
376
+ # parse where conditions
377
+ conditions = @parser.parse(where_clause).tree[:conditions]
378
+
379
+ # filter array
380
+ @my_array.select do |geo_obj|
381
+ is_valid = true
382
+ conditions.each do |condition|
383
+ field = condition[:field]
384
+ compare_value = add_on || condition[:value]
385
+ operator = condition[:operator].to_s
386
+
387
+ # check that key exists
388
+ is_valid = geo_obj.key?(field)
389
+ break unless is_valid
390
+
391
+ # compare to value
392
+ if operator == '<'
393
+ is_valid = geo_obj[field] < compare_value
394
+ elsif operator == '<='
395
+ is_valid = geo_obj[field] <= compare_value
396
+ elsif operator == '>'
397
+ is_valid = geo_obj[field] > compare_value
398
+ elsif operator == '>='
399
+ is_valid = geo_obj[field] >= compare_value
400
+ elsif operator == '='
401
+ is_valid = geo_obj[field] == compare_value
402
+ else
403
+ fail "#{operator} comparison not supported by MapPLZ on arrays"
404
+ end
405
+ break unless is_valid
406
+ end
407
+ is_valid
408
+ end
409
+ end
410
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mapplz
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Nick Doiron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov-rcov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: actionpack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 3.2.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 3.2.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 3.2.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: activemodel
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 3.2.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 3.2.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: railties
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 3.2.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: 3.2.0
111
+ description: Quick and easy mapping with MapPLZ
112
+ email:
113
+ - ndoiron@mapmeld.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - lib/mapplz.rb
119
+ - app/assets/javascripts/mapplz/mapplz.js
120
+ - app/assets/stylesheets/mapplz/mapplz.css.erb
121
+ - app/views/mapplz/map.html.haml
122
+ - README.md
123
+ homepage: https://github.com/mapmeld/mapplz-ruby
124
+ licenses:
125
+ - BSD
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.1.10
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Quick and easy mapping with MapPLZ
147
+ test_files: []