mapplz 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []