├── .gitignore
├── dist
├── distance
│ ├── distance-icon.png
│ ├── leaflet-control.css
│ └── leaflet-control.js
├── validators
│ ├── jquery-sources-control.js
│ ├── jquery-errors-control.js
│ ├── jstree-errors-control.js
│ └── leaflet-layer.js
└── weather
│ └── leaflet-layer.js
├── package.json
├── Cakefile
├── LICENSE
├── src
├── validators
│ ├── jstree-errors-control.coffee
│ ├── jquery-sources-control.coffee
│ ├── jquery-errors-control.coffee
│ └── leaflet-layer.coffee
├── distance
│ └── leaflet-control.coffee
└── weather
│ └── leaflet-layer.coffee
├── README.md
└── examples
├── distance.html
├── weather.html
├── weather-imperial.html
├── validators.html
└── validators-jstree.html
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 |
--------------------------------------------------------------------------------
/dist/distance/distance-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alno/osm-js-libs/HEAD/dist/distance/distance-icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "osm-js-libs",
3 | "description": "JavaScript libraries for OpenStreetMap applications",
4 | "version": "0.1.0",
5 | "author": "alno",
6 | "licenses":
7 | [{
8 | "type": "MIT",
9 | "url": "https://github.com/maccman/spine/blob/master/LICENSE"
10 | }],
11 | "repository": {
12 | "type" : "git",
13 | "url": "http://github.com/alno/osm-js-libs.git"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Cakefile:
--------------------------------------------------------------------------------
1 | {print} = require 'util'
2 | {spawn} = require 'child_process'
3 |
4 | task 'build', 'Build dist/ from src/', ->
5 | coffee = spawn 'coffee', ['-c', '-o', 'dist', 'src']
6 | coffee.stderr.on 'data', (data) ->
7 | process.stderr.write data.toString()
8 | coffee.stdout.on 'data', (data) ->
9 | print data.toString()
10 | coffee.on 'exit', (code) ->
11 | callback?() if code is 0
12 |
13 | task 'watch', 'Watch src/ for changes', ->
14 | coffee = spawn 'coffee', ['-w', '-c', '-o', 'dist', 'src']
15 | coffee.stderr.on 'data', (data) ->
16 | process.stderr.write data.toString()
17 | coffee.stdout.on 'data', (data) ->
18 | print data.toString()
19 |
--------------------------------------------------------------------------------
/dist/distance/leaflet-control.css:
--------------------------------------------------------------------------------
1 | .leaflet-control-distance {
2 | -moz-border-radius: 7px;
3 | -webkit-border-radius: 7px;
4 | border-radius: 7px;
5 | padding: 5px;
6 | background: rgba(0, 0, 0, 0.25);
7 | }
8 |
9 | .leaflet-control-distance a {
10 | background-color: rgba(255, 255, 255, 0.75);
11 | background-position: 50% 50%;
12 | background-repeat: no-repeat;
13 | background-image: url(distance-icon.png);
14 | display: block;
15 | -moz-border-radius: 4px;
16 | -webkit-border-radius: 4px;
17 | border-radius: 4px;
18 | width: 19px;
19 | height: 19px;
20 | }
21 |
22 | .leaflet-control-distance a:hover {
23 | background-color: #fff;
24 | }
25 |
26 | .leaflet-control-distance a.active {
27 | background-color: rgba(255, 255, 0, 0.75);
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Alexey Noskov (alexey.noskov@gmail.com)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/src/validators/jstree-errors-control.coffee:
--------------------------------------------------------------------------------
1 |
2 | class JstreeValidatorErrorsControl
3 |
4 | constructor: (@elem, @layer, @options = {}) ->
5 | @elem.jstree(
6 | plugins: [ "json_data", "checkbox", "ui" ]
7 | json_data:
8 | data:
9 | @nodeJson(e) for e in @options.errors
10 | checkbox:
11 | override_ui: true
12 | ).bind("change_state.check_box.jstree", @stateChanged).bind("loaded.jstree", @stateChanged)
13 |
14 | @tree = jQuery.jstree._reference(@elem)
15 | @stateChanged()
16 |
17 | nodeJson: (err) ->
18 | data: err.name
19 | attr:
20 | "data-error-type": err.type
21 | children: @nodeJson(chd) for chd in (err.children or [])
22 |
23 | stateChanged: =>
24 | for node in @tree.get_checked(null, true) when $(node).data('error-type')
25 | @layer.enableError $(node).data('error-type')
26 |
27 | for node in @tree.get_unchecked(null, true) when $(node).data('error-type')
28 | @layer.disableError $(node).data('error-type')
29 |
30 | jQuery.fn.validatorErrorsControl = (layer, options) ->
31 | @each ->
32 | new JstreeValidatorErrorsControl($(@), layer, options)
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript libraries for OpenStreetMap applications
2 |
3 | ## Usage
4 |
5 | ### Validators Leaflet layer
6 |
7 | Add validators layer to your Leaflet map by:
8 |
9 | map.addLayer(new OsmJs.Validators.LeafletLayer(config))
10 |
11 | Where example config is:
12 |
13 | {
14 | validators: [{
15 | "name": "Отладочный валидатор",
16 | "url": "http://alno.name:4567/validate?minlat={minlat}&minlon={minlon}&maxlat={maxlat}&maxlon={maxlon}",
17 | "offset_limit": true,
18 | "jsonp": true,
19 | "types": {
20 | "test_error": {"text": "Тестовая ошибка"}
21 | }
22 | }]
23 | }
24 |
25 | ### OpenWeatherMap weather layer
26 |
27 | ### Leaflet distance control
28 |
29 | Icon for distance control from here: http://findicons.com/icon/178351/ruler_2
30 |
31 | ## Building
32 |
33 | Install coffee-script first:
34 |
35 | sudo npm install --global coffee-script
36 |
37 | Then call:
38 |
39 | cake build
40 |
41 | That's all, now you have your updated library in dist/
42 |
43 | ## Contributors
44 |
45 | * Alexey Noskov ({alno}[https://github.com/alno])
46 |
47 | Copyright © 2012 Alexey Noskov, released under the {MIT license}[http://www.opensource.org/licenses/MIT]
48 |
--------------------------------------------------------------------------------
/src/validators/jquery-sources-control.coffee:
--------------------------------------------------------------------------------
1 |
2 | class JqueryValidatorSourcesControl
3 |
4 | constructor: (@elem, @layer, @options = {}) ->
5 | @sources = []
6 |
7 | if @options.sources
8 | for source in @options.sources
9 | @sources.push(source)
10 |
11 | for url, source of @layer.sources when @sources.indexOf(source) < 0
12 | @sources.push(source)
13 |
14 | @layer.on 'sourceadd', (e) =>
15 | @sources.push(e.source) if @sources.indexOf(e.source) < 0
16 | @update()
17 |
18 | @layer.on 'sourceremove', @update, @
19 |
20 | @update()
21 |
22 | update: ->
23 | @elem.html('')
24 |
25 | for source in @sources
26 | @elem.append(@buildListItem(source))
27 |
28 | buildListItem: (source) ->
29 | cb = $('')
30 | cb.attr('checked', 'checked') if @layer.sources[source.url]
31 | cb.change =>
32 | if cb.attr('checked')
33 | @layer.addSource(source)
34 | else
35 | @layer.removeSource(source)
36 |
37 | tx = $('')
38 | tx.text(source.name)
39 |
40 | li = $('
')
41 | li.append(cb)
42 | li.append(tx)
43 | li
44 |
45 |
46 | jQuery.fn.validatorSourcesControl = (layer, options) ->
47 | @each ->
48 | new JqueryValidatorSourcesControl($(@), layer, options)
49 |
--------------------------------------------------------------------------------
/examples/distance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Leaflet distance example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/examples/weather.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Leaflet weather example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/validators/jquery-errors-control.coffee:
--------------------------------------------------------------------------------
1 |
2 | class JqueryValidatorErrorsControl
3 |
4 | constructor: (@elem, @layer, @options = {}) ->
5 | @errors = @options.errors
6 |
7 | @update()
8 |
9 | update: ->
10 | @elem.html('')
11 |
12 | for error in @errors
13 | @elem.append(@buildListItem(error))
14 |
15 | buildListItem: (error) ->
16 | ul = $('')
17 |
18 | for childErr in (error.children or [])
19 | ul.append(@buildListItem(childErr))
20 |
21 | cb = $('')
22 | cb.data('type', error.type)
23 | cb.attr('checked', 'checked') unless error.type and @layer.disabledErrors.indexOf(error.type) >= 0
24 | cb.change =>
25 | if cb.attr('checked')
26 | ul.find('input').removeAttr('disabled')
27 | else
28 | ul.find('input').attr('disabled','true')
29 |
30 | for e in li.find('input')
31 | ee = $(e)
32 | if type = ee.data('type')
33 | if !ee.attr('disabled') && ee.attr('checked')
34 | @layer.enableError(type)
35 | else
36 | @layer.disableError(type)
37 |
38 | tx = $('')
39 | tx.text(error.name)
40 |
41 | li = $('')
42 | li.append(cb)
43 | li.append(tx)
44 | li.append(ul)
45 | li
46 |
47 | jQuery.fn.validatorErrorsControl = (layer, options) ->
48 | @each ->
49 | new JqueryValidatorErrorsControl($(@), layer, options)
50 |
--------------------------------------------------------------------------------
/examples/weather-imperial.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Leaflet weather example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/dist/validators/jquery-sources-control.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var JqueryValidatorSourcesControl;
4 |
5 | JqueryValidatorSourcesControl = (function() {
6 |
7 | JqueryValidatorSourcesControl.name = 'JqueryValidatorSourcesControl';
8 |
9 | function JqueryValidatorSourcesControl(elem, layer, options) {
10 | var source, url, _i, _len, _ref, _ref1,
11 | _this = this;
12 | this.elem = elem;
13 | this.layer = layer;
14 | this.options = options != null ? options : {};
15 | this.sources = [];
16 | if (this.options.sources) {
17 | _ref = this.options.sources;
18 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
19 | source = _ref[_i];
20 | this.sources.push(source);
21 | }
22 | }
23 | _ref1 = this.layer.sources;
24 | for (url in _ref1) {
25 | source = _ref1[url];
26 | if (this.sources.indexOf(source) < 0) {
27 | this.sources.push(source);
28 | }
29 | }
30 | this.layer.on('sourceadd', function(e) {
31 | if (_this.sources.indexOf(e.source) < 0) {
32 | _this.sources.push(e.source);
33 | }
34 | return _this.update();
35 | });
36 | this.layer.on('sourceremove', this.update, this);
37 | this.update();
38 | }
39 |
40 | JqueryValidatorSourcesControl.prototype.update = function() {
41 | var source, _i, _len, _ref, _results;
42 | this.elem.html('');
43 | _ref = this.sources;
44 | _results = [];
45 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
46 | source = _ref[_i];
47 | _results.push(this.elem.append(this.buildListItem(source)));
48 | }
49 | return _results;
50 | };
51 |
52 | JqueryValidatorSourcesControl.prototype.buildListItem = function(source) {
53 | var cb, li, tx,
54 | _this = this;
55 | cb = $('');
56 | if (this.layer.sources[source.url]) {
57 | cb.attr('checked', 'checked');
58 | }
59 | cb.change(function() {
60 | if (cb.attr('checked')) {
61 | return _this.layer.addSource(source);
62 | } else {
63 | return _this.layer.removeSource(source);
64 | }
65 | });
66 | tx = $('');
67 | tx.text(source.name);
68 | li = $('');
69 | li.append(cb);
70 | li.append(tx);
71 | return li;
72 | };
73 |
74 | return JqueryValidatorSourcesControl;
75 |
76 | })();
77 |
78 | jQuery.fn.validatorSourcesControl = function(layer, options) {
79 | return this.each(function() {
80 | return new JqueryValidatorSourcesControl($(this), layer, options);
81 | });
82 | };
83 |
84 | }).call(this);
85 |
--------------------------------------------------------------------------------
/dist/validators/jquery-errors-control.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var JqueryValidatorErrorsControl;
4 |
5 | JqueryValidatorErrorsControl = (function() {
6 |
7 | JqueryValidatorErrorsControl.name = 'JqueryValidatorErrorsControl';
8 |
9 | function JqueryValidatorErrorsControl(elem, layer, options) {
10 | this.elem = elem;
11 | this.layer = layer;
12 | this.options = options != null ? options : {};
13 | this.errors = this.options.errors;
14 | this.update();
15 | }
16 |
17 | JqueryValidatorErrorsControl.prototype.update = function() {
18 | var error, _i, _len, _ref, _results;
19 | this.elem.html('');
20 | _ref = this.errors;
21 | _results = [];
22 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
23 | error = _ref[_i];
24 | _results.push(this.elem.append(this.buildListItem(error)));
25 | }
26 | return _results;
27 | };
28 |
29 | JqueryValidatorErrorsControl.prototype.buildListItem = function(error) {
30 | var cb, childErr, li, tx, ul, _i, _len, _ref,
31 | _this = this;
32 | ul = $('');
33 | _ref = error.children || [];
34 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
35 | childErr = _ref[_i];
36 | ul.append(this.buildListItem(childErr));
37 | }
38 | cb = $('');
39 | cb.data('type', error.type);
40 | if (!(error.type && this.layer.disabledErrors.indexOf(error.type) >= 0)) {
41 | cb.attr('checked', 'checked');
42 | }
43 | cb.change(function() {
44 | var e, ee, type, _j, _len1, _ref1, _results;
45 | if (cb.attr('checked')) {
46 | ul.find('input').removeAttr('disabled');
47 | } else {
48 | ul.find('input').attr('disabled', 'true');
49 | }
50 | _ref1 = li.find('input');
51 | _results = [];
52 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
53 | e = _ref1[_j];
54 | ee = $(e);
55 | if (type = ee.data('type')) {
56 | if (!ee.attr('disabled') && ee.attr('checked')) {
57 | _results.push(_this.layer.enableError(type));
58 | } else {
59 | _results.push(_this.layer.disableError(type));
60 | }
61 | } else {
62 | _results.push(void 0);
63 | }
64 | }
65 | return _results;
66 | });
67 | tx = $('');
68 | tx.text(error.name);
69 | li = $('');
70 | li.append(cb);
71 | li.append(tx);
72 | li.append(ul);
73 | return li;
74 | };
75 |
76 | return JqueryValidatorErrorsControl;
77 |
78 | })();
79 |
80 | jQuery.fn.validatorErrorsControl = function(layer, options) {
81 | return this.each(function() {
82 | return new JqueryValidatorErrorsControl($(this), layer, options);
83 | });
84 | };
85 |
86 | }).call(this);
87 |
--------------------------------------------------------------------------------
/dist/validators/jstree-errors-control.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var JstreeValidatorErrorsControl,
4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
5 |
6 | JstreeValidatorErrorsControl = (function() {
7 |
8 | JstreeValidatorErrorsControl.name = 'JstreeValidatorErrorsControl';
9 |
10 | function JstreeValidatorErrorsControl(elem, layer, options) {
11 | var e;
12 | this.elem = elem;
13 | this.layer = layer;
14 | this.options = options != null ? options : {};
15 | this.stateChanged = __bind(this.stateChanged, this);
16 |
17 | this.elem.jstree({
18 | plugins: ["json_data", "checkbox", "ui"],
19 | json_data: {
20 | data: (function() {
21 | var _i, _len, _ref, _results;
22 | _ref = this.options.errors;
23 | _results = [];
24 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
25 | e = _ref[_i];
26 | _results.push(this.nodeJson(e));
27 | }
28 | return _results;
29 | }).call(this)
30 | },
31 | checkbox: {
32 | override_ui: true
33 | }
34 | }).bind("change_state.check_box.jstree", this.stateChanged).bind("loaded.jstree", this.stateChanged);
35 | this.tree = jQuery.jstree._reference(this.elem);
36 | this.stateChanged();
37 | }
38 |
39 | JstreeValidatorErrorsControl.prototype.nodeJson = function(err) {
40 | var chd;
41 | return {
42 | data: err.name,
43 | attr: {
44 | "data-error-type": err.type
45 | },
46 | children: (function() {
47 | var _i, _len, _ref, _results;
48 | _ref = err.children || [];
49 | _results = [];
50 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
51 | chd = _ref[_i];
52 | _results.push(this.nodeJson(chd));
53 | }
54 | return _results;
55 | }).call(this)
56 | };
57 | };
58 |
59 | JstreeValidatorErrorsControl.prototype.stateChanged = function() {
60 | var node, _i, _j, _len, _len1, _ref, _ref1, _results;
61 | _ref = this.tree.get_checked(null, true);
62 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
63 | node = _ref[_i];
64 | if ($(node).data('error-type')) {
65 | this.layer.enableError($(node).data('error-type'));
66 | }
67 | }
68 | _ref1 = this.tree.get_unchecked(null, true);
69 | _results = [];
70 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
71 | node = _ref1[_j];
72 | if ($(node).data('error-type')) {
73 | _results.push(this.layer.disableError($(node).data('error-type')));
74 | }
75 | }
76 | return _results;
77 | };
78 |
79 | return JstreeValidatorErrorsControl;
80 |
81 | })();
82 |
83 | jQuery.fn.validatorErrorsControl = function(layer, options) {
84 | return this.each(function() {
85 | return new JstreeValidatorErrorsControl($(this), layer, options);
86 | });
87 | };
88 |
89 | }).call(this);
90 |
--------------------------------------------------------------------------------
/src/distance/leaflet-control.coffee:
--------------------------------------------------------------------------------
1 | DistancePopup = L.Popup.extend
2 |
3 | _close: () -> null
4 |
5 | class DistancePath
6 |
7 | constructor: (@map, @control) ->
8 | @active = false
9 | @poly = new L.Polyline([])
10 | @poly.on 'dblclick', @remove
11 | @poly.on 'click', =>
12 | if @active
13 | @passivate()
14 | else
15 | @activate()
16 | @poly.on 'edit', @onEdited
17 |
18 | @map.addLayer(@poly)
19 |
20 | activate: =>
21 | @active = true
22 | @poly.editing.enable() if @poly.editing?
23 | @poly.setStyle @control.options.activeStyle
24 |
25 | @map.on 'click', @onMapClick
26 | @control.activate(@)
27 |
28 | passivate: =>
29 | @active = false
30 | @poly.editing.disable() if @poly?.editing?
31 | @poly.setStyle @control.options.passiveStyle
32 |
33 | @map.off 'click', @onMapClick
34 | @control.passivate()
35 |
36 | remove: =>
37 | @passivate()
38 | @map.removeLayer(@poly)
39 | @map.removeLayer(@popup) if @popup
40 |
41 | onMapClick: (e) =>
42 | @poly.addLatLng(e.latlng)
43 |
44 | unless @popup
45 | @popup = new DistancePopup()
46 | @popup.setLatLng(e.latlng)
47 | @map.addLayer(@popup)
48 |
49 | if @poly.editing?
50 | @poly.editing.disable()
51 | @poly.editing.enable()
52 | @poly.fire('edit')
53 |
54 | @onEdited()
55 |
56 | onEdited: =>
57 | points = @poly.getLatLngs()
58 |
59 | @popup.setContent(@formatDistance(@calculateDistance(points)))
60 | @popup.setLatLng(points[points.length-1])
61 |
62 | calculateDistance: (points) ->
63 | len = 0
64 |
65 | if points.length > 1
66 | for i in [1..points.length-1]
67 | len += points[i-1].distanceTo points[i]
68 |
69 | len
70 |
71 | formatDistance: (dist) ->
72 | if dist > 2000
73 | Math.round(dist/100)/10 + " km"
74 | else if dist > 5
75 | Math.round(dist) + " m"
76 | else
77 | dist + " m"
78 |
79 | class DistanceControl
80 |
81 | constructor: (@map, @options) ->
82 | @map = map
83 | @container = L.DomUtil.create('div', 'leaflet-control-distance')
84 | @link = L.DomUtil.create('a', '', @container)
85 | @link.href = '#'
86 | @link.title = "Start measuring distance"
87 |
88 | L.DomEvent.addListener(@link, 'click', L.DomEvent.stopPropagation)
89 | L.DomEvent.addListener(@link, 'click', L.DomEvent.preventDefault)
90 | L.DomEvent.addListener(@link, 'click', @toggleMeasure)
91 |
92 | toggleMeasure: =>
93 | if @path
94 | @path.passivate()
95 | else
96 | new DistancePath(@map, @).activate()
97 |
98 | activate: (path) ->
99 | if @path and @path != path
100 | @path.passivate()
101 |
102 | L.DomUtil.addClass @link, 'active'
103 |
104 | @path = path
105 |
106 | passivate: ->
107 | L.DomUtil.removeClass @link, 'active'
108 | @path = null
109 |
110 | @L.Control.Distance = @L.Control.extend
111 |
112 | options:
113 | position: 'topleft'
114 |
115 | activeStyle:
116 | color: 'red'
117 |
118 | passiveStyle:
119 | color: 'blue'
120 |
121 | onAdd: (map) ->
122 | new DistanceControl(map, @options).container
123 |
--------------------------------------------------------------------------------
/dist/distance/leaflet-control.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var DistanceControl, DistancePath, DistancePopup,
4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
5 |
6 | DistancePopup = L.Popup.extend({
7 | _close: function() {
8 | return null;
9 | }
10 | });
11 |
12 | DistancePath = (function() {
13 |
14 | DistancePath.name = 'DistancePath';
15 |
16 | function DistancePath(map, control) {
17 | var _this = this;
18 | this.map = map;
19 | this.control = control;
20 | this.onEdited = __bind(this.onEdited, this);
21 |
22 | this.onMapClick = __bind(this.onMapClick, this);
23 |
24 | this.remove = __bind(this.remove, this);
25 |
26 | this.passivate = __bind(this.passivate, this);
27 |
28 | this.activate = __bind(this.activate, this);
29 |
30 | this.active = false;
31 | this.poly = new L.Polyline([]);
32 | this.poly.on('dblclick', this.remove);
33 | this.poly.on('click', function() {
34 | if (_this.active) {
35 | return _this.passivate();
36 | } else {
37 | return _this.activate();
38 | }
39 | });
40 | this.poly.on('edit', this.onEdited);
41 | this.map.addLayer(this.poly);
42 | }
43 |
44 | DistancePath.prototype.activate = function() {
45 | this.active = true;
46 | if (this.poly.editing != null) {
47 | this.poly.editing.enable();
48 | }
49 | this.poly.setStyle(this.control.options.activeStyle);
50 | this.map.on('click', this.onMapClick);
51 | return this.control.activate(this);
52 | };
53 |
54 | DistancePath.prototype.passivate = function() {
55 | var _ref;
56 | this.active = false;
57 | if (((_ref = this.poly) != null ? _ref.editing : void 0) != null) {
58 | this.poly.editing.disable();
59 | }
60 | this.poly.setStyle(this.control.options.passiveStyle);
61 | this.map.off('click', this.onMapClick);
62 | return this.control.passivate();
63 | };
64 |
65 | DistancePath.prototype.remove = function() {
66 | this.passivate();
67 | this.map.removeLayer(this.poly);
68 | if (this.popup) {
69 | return this.map.removeLayer(this.popup);
70 | }
71 | };
72 |
73 | DistancePath.prototype.onMapClick = function(e) {
74 | this.poly.addLatLng(e.latlng);
75 | if (!this.popup) {
76 | this.popup = new DistancePopup();
77 | this.popup.setLatLng(e.latlng);
78 | this.map.addLayer(this.popup);
79 | }
80 | if (this.poly.editing != null) {
81 | this.poly.editing.disable();
82 | this.poly.editing.enable();
83 | this.poly.fire('edit');
84 | }
85 | return this.onEdited();
86 | };
87 |
88 | DistancePath.prototype.onEdited = function() {
89 | var points;
90 | points = this.poly.getLatLngs();
91 | this.popup.setContent(this.formatDistance(this.calculateDistance(points)));
92 | return this.popup.setLatLng(points[points.length - 1]);
93 | };
94 |
95 | DistancePath.prototype.calculateDistance = function(points) {
96 | var i, len, _i, _ref;
97 | len = 0;
98 | if (points.length > 1) {
99 | for (i = _i = 1, _ref = points.length - 1; 1 <= _ref ? _i <= _ref : _i >= _ref; i = 1 <= _ref ? ++_i : --_i) {
100 | len += points[i - 1].distanceTo(points[i]);
101 | }
102 | }
103 | return len;
104 | };
105 |
106 | DistancePath.prototype.formatDistance = function(dist) {
107 | if (dist > 2000) {
108 | return Math.round(dist / 100) / 10 + " km";
109 | } else if (dist > 5) {
110 | return Math.round(dist) + " m";
111 | } else {
112 | return dist + " m";
113 | }
114 | };
115 |
116 | return DistancePath;
117 |
118 | })();
119 |
120 | DistanceControl = (function() {
121 |
122 | DistanceControl.name = 'DistanceControl';
123 |
124 | function DistanceControl(map, options) {
125 | this.map = map;
126 | this.options = options;
127 | this.toggleMeasure = __bind(this.toggleMeasure, this);
128 |
129 | this.map = map;
130 | this.container = L.DomUtil.create('div', 'leaflet-control-distance');
131 | this.link = L.DomUtil.create('a', '', this.container);
132 | this.link.href = '#';
133 | this.link.title = "Start measuring distance";
134 | L.DomEvent.addListener(this.link, 'click', L.DomEvent.stopPropagation);
135 | L.DomEvent.addListener(this.link, 'click', L.DomEvent.preventDefault);
136 | L.DomEvent.addListener(this.link, 'click', this.toggleMeasure);
137 | }
138 |
139 | DistanceControl.prototype.toggleMeasure = function() {
140 | if (this.path) {
141 | return this.path.passivate();
142 | } else {
143 | return new DistancePath(this.map, this).activate();
144 | }
145 | };
146 |
147 | DistanceControl.prototype.activate = function(path) {
148 | if (this.path && this.path !== path) {
149 | this.path.passivate();
150 | }
151 | L.DomUtil.addClass(this.link, 'active');
152 | return this.path = path;
153 | };
154 |
155 | DistanceControl.prototype.passivate = function() {
156 | L.DomUtil.removeClass(this.link, 'active');
157 | return this.path = null;
158 | };
159 |
160 | return DistanceControl;
161 |
162 | })();
163 |
164 | this.L.Control.Distance = this.L.Control.extend({
165 | options: {
166 | position: 'topleft',
167 | activeStyle: {
168 | color: 'red'
169 | },
170 | passiveStyle: {
171 | color: 'blue'
172 | }
173 | },
174 | onAdd: function(map) {
175 | return new DistanceControl(map, this.options).container;
176 | }
177 | });
178 |
179 | }).call(this);
180 |
--------------------------------------------------------------------------------
/src/validators/leaflet-layer.coffee:
--------------------------------------------------------------------------------
1 |
2 | intersects = (arr1, arr2) ->
3 | for el in arr1 when arr2.indexOf(el) >= 0
4 | return true
5 |
6 | false
7 |
8 | intersectsKeys = (arr, hash) ->
9 | for el in arr when hash[el]
10 | return true
11 |
12 | false
13 |
14 | Layer = L.Class.extend
15 |
16 | includes: L.Mixin.Events
17 |
18 | initialize: (@options = {})->
19 | @sources = {}
20 | @sourceLayers = {}
21 | @sourceRequests = {}
22 | @disabledErrors = []
23 | @i18n = @options.i18n or { error_info: 'More error info', errors: 'Errors', objects: 'Objects', params: 'Params', edit_in_potlatch: 'Edit in Potlatch', edit_in_josm: 'Edit in JOSM', created_at: 'Created at', updated_at: 'Updated at' }
24 |
25 | for source in (@options.sources or [])
26 | @addSource(source)
27 |
28 | disableError: (error) ->
29 | if @disabledErrors.indexOf(error) < 0
30 | @disabledErrors.push(error)
31 | @update()
32 |
33 | enableError: (error) ->
34 | if (idx = @disabledErrors.indexOf(error)) >= 0
35 | @disabledErrors.splice(idx, 1)
36 | @update()
37 |
38 | addSource: (source) ->
39 | if @sources[source.url]
40 | @sources[source.url] = source
41 | @updateSource(source) if @map
42 |
43 | @fire('sourcechange', {source: source})
44 | else
45 | @sourceLayers[source.url] = new L.LayerGroup()
46 | @sources[source.url] = source
47 |
48 | if @sourceRequests[source.url]
49 | @sourceRequests[source.url].abort()
50 | delete @sourceRequests[source.url]
51 |
52 | if @map
53 | @map.addLayer(@sourceLayers[source.url])
54 | @updateSource(source)
55 |
56 | @fire('sourceadd', {source: source})
57 |
58 | removeSource: (source) ->
59 | if @sources[source.url]
60 | @map.removeLayer(@sourceLayers[source.url]) if @map
61 |
62 | delete @sourceLayers[source.url]
63 | delete @sources[source.url]
64 |
65 | if @sourceRequests[source.url]
66 | @sourceRequests[source.url].abort()
67 | delete @sourceRequests[source.url]
68 |
69 | @fire('sourceremove', {source: source})
70 |
71 | onAdd: (map) ->
72 | @map = map
73 |
74 | for key, layer of @sourceLayers
75 | map.addLayer(layer)
76 |
77 | map.on('moveend', @update, @)
78 |
79 | @update()
80 |
81 | onRemove: (map) ->
82 | map.off('moveend', @update, @)
83 |
84 | for key, layer of @sourceLayers
85 | map.removeLayer(layer)
86 |
87 | @map = undefined
88 |
89 | update: ->
90 | return unless @map
91 |
92 | for url, req of @sourceRequests
93 | req.abort()
94 |
95 | @sourceRequests = {}
96 |
97 | for url, source of @sources
98 | @updateSource(source)
99 |
100 | updateSource: (source) ->
101 | bounds = @map.getBounds()
102 | sw = bounds.getSouthWest()
103 | ne = bounds.getNorthEast()
104 |
105 | url = source.url
106 | .replace('{minlat}', sw.lat)
107 | .replace('{maxlat}', ne.lat)
108 | .replace('{minlon}', sw.lng)
109 | .replace('{maxlon}', ne.lng)
110 | .replace('{filtered_types}', @getErrorTypes(source).join(','))
111 |
112 | @sourceRequests[source.url] = Layer.Utils.request url, source, (data) =>
113 | delete @sourceRequests[source.url]
114 |
115 | layer = @sourceLayers[source.url]
116 | @map.removeLayer(layer)
117 |
118 | for res in data.results when res.type
119 | res.types = [res.type]
120 |
121 | layer.clearLayers()
122 | layer.addLayer(@buildResult(source, res)) for res in data.results when intersectsKeys(res.types, source.types) and not intersects(res.types, @disabledErrors)
123 |
124 | @map.addLayer(layer)
125 |
126 | getErrorTypes: (source) ->
127 | for type, desc of source.types when @disabledErrors.indexOf(type) < 0
128 | type
129 |
130 | buildResult: (source, res) ->
131 | bounds = new L.LatLngBounds()
132 | resLayer = new L.GeoJSON(type: 'Feature', geometry: res.geometry)
133 |
134 | Layer.Utils.extendBounds(bounds, resLayer)
135 |
136 | center = bounds.getCenter()
137 | sw = bounds.getSouthWest()
138 | ne = bounds.getNorthEast()
139 |
140 | popupText = ""
141 | popupText += "
#{@i18n.errors}
"
142 | popupText += "
"
143 | for type in res.types when source.types[type]?.text
144 | errorTemplate = source.types[type].text
145 | errorData = res.params or {}
146 |
147 | popupText += "- "
148 | popupText += errorTemplate.replace /\{ *([\w_]+) *\}/g, (str, key) ->
149 | errorData[key]
150 | popupText += "
"
151 | popupText += "
"
152 |
153 | if @options.dateFormat
154 | popupText += "
"
155 | popupText += "#{@i18n.created_at}: #{@formatDate(new Date(res.created_at))}
" if res.created_at
156 | popupText += "#{@i18n.updated_at}: #{@formatDate(new Date(res.updated_at))}
" if res.updated_at
157 | popupText += "
"
158 |
159 | popupText += "
"
160 | popupText += "#{@i18n.error_info}
" if res.url
161 | popupText += "#{@i18n.edit_in_josm}
"
162 | popupText += "#{@i18n.edit_in_potlatch}
"
163 | popupText += "
"
164 |
165 | if res.objects
166 | popupText += "
#{@i18n.objects}
"
167 | popupText += "
"
168 | for obj in res.objects
169 | popupText += "- #{obj.join('-')}
"
170 | popupText += "
"
171 |
172 | if res.params
173 | popupText += "
#{@i18n.params}
"
174 | popupText += "
"
175 | for key, value of res.params
176 | popupText += "- #{key}: #{value}
"
177 | popupText += "
"
178 |
179 | popupText += "
"
180 |
181 | resLayer.bindPopup(popupText)
182 | resLayer
183 |
184 | formatDate: (date) ->
185 | @options.dateFormat
186 | .replace("DD", (if date.getDate() < 10 then '0' else '') + date.getDate()) # Pad with '0' if needed
187 | .replace("MM", (if date.getMonth() < 9 then '0' else '') + (date.getMonth() + 1)) # Months are zero-based
188 | .replace("YYYY", date.getFullYear())
189 |
190 | Layer.Utils =
191 | callbacks: {}
192 | callbackCounter: 0
193 |
194 | extendBounds: (bounds, l) ->
195 | if l.getBounds
196 | bounds.extend l.getBounds().getSouthWest()
197 | bounds.extend l.getBounds().getNorthEast()
198 | else if l.getLatLng
199 | bounds.extend l.getLatLng()
200 | else if l._iterateLayers
201 | l._iterateLayers (c) ->
202 | Layer.Utils.extendBounds(bounds, c)
203 | else
204 | console.log(["Can't determine layer bounds", l])
205 |
206 | request: (url, source, cb) ->
207 | if source.jsonp
208 | @requestJsonp url, cb
209 | else
210 | @requestXhr url, cb
211 |
212 | requestXhr: (url, cb) ->
213 | xhr = new XMLHttpRequest()
214 | xhr.open 'GET', url, true
215 | xhr.onreadystatechange = ->
216 | if xhr.readyState == 4
217 | if xhr.status == 200
218 | cb(eval("(#{xhr.responseText})"))
219 |
220 | xhr.send()
221 | xhr
222 |
223 | requestJsonp: (url, cb) ->
224 | el = document.createElement('script')
225 | counter = (@callbackCounter += 1)
226 | callback = "OsmJs.Validators.LeafletLayer.Utils.callbacks[#{counter}]"
227 |
228 | abort = ->
229 | el.parentNode.removeChild(el) if el.parentNode
230 |
231 | @callbacks[counter] = (data) =>
232 | el.parentNode.removeChild(el) if el.parentNode
233 | delete @callbacks[counter]
234 | cb(data)
235 |
236 | delim = if url.indexOf('?') >= 0
237 | '&'
238 | else
239 | '?'
240 |
241 | el.src = "#{url}#{delim}callback=#{callback}"
242 | document.getElementsByTagName('body')[0].appendChild(el)
243 |
244 | {abort: abort}
245 |
246 | @OsmJs = {} unless @OsmJs
247 | @OsmJs.Validators = {} unless @OsmJs.Validators
248 | @OsmJs.Validators.LeafletLayer = Layer
249 |
--------------------------------------------------------------------------------
/examples/validators.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Leaflet validators example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
28 |
29 |
30 |
31 |
32 | Используемые источники:
33 |
34 |
35 | Отображаемые ошибки:
36 |
37 |
38 |
214 |
215 |
216 |
--------------------------------------------------------------------------------
/examples/validators-jstree.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Leaflet validators example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
32 |
33 |
34 |
35 |
36 | Используемые источники:
37 |
38 |
39 | Отображаемые ошибки:
40 |
41 |
42 |
216 |
217 |
218 |
--------------------------------------------------------------------------------
/src/weather/leaflet-layer.coffee:
--------------------------------------------------------------------------------
1 | Icon = L.Icon.extend
2 |
3 | options:
4 | popupAnchor: new L.Point(0, -25)
5 |
6 | initialize: (options) ->
7 | L.Util.setOptions(this, options)
8 |
9 | createIcon: ->
10 | div = document.createElement('div')
11 | div.className = 'leaflet-marker-icon weather-icon'
12 | div.style['margin'] = '-30px 0px 0px -30px'
13 | div.style['width'] = '60px'
14 | div.style['height'] = '20px'
15 | div.style['padding'] = "#{@options.textOffset}px 0px 0px 0px"
16 | div.style['background'] = "url(#{@options.image}) no-repeat center top"
17 | div.style['textAlign'] = 'center'
18 |
19 | span = document.createElement('span')
20 | span.innerHTML = @options.text
21 |
22 | div.appendChild(span)
23 | div
24 |
25 | createShadow: -> null
26 |
27 | UnitFormatters =
28 | metric:
29 | temperature: (k, digits) ->
30 | p = Math.pow(10, digits)
31 | c = k - 273.15 # Convert kelvin degrees to celsius
32 |
33 | "#{Math.round(c * p) / p} °C"
34 |
35 | speed: (v) ->
36 | "#{v} m/s"
37 |
38 | height: (v) ->
39 | "#{v} mm"
40 |
41 | imperial:
42 | temperature: (k, digits) ->
43 | p = Math.pow(10, digits)
44 | f = (k - 273.15) * 1.8 + 32 # Convert kelvin degrees to fahrenheit
45 |
46 | "#{Math.round(f * p) / p} °F"
47 |
48 | speed: (v) ->
49 | v = Math.round(v*2.237) # Convert m/s to mph
50 | "#{v} mph"
51 |
52 | height: (v) ->
53 | v = Math.round(v/1.27) / 20 # Convert mm to inches
54 | "#{v} in"
55 |
56 | Layer = L.Class.extend
57 |
58 | defaultI18n:
59 | en:
60 | currentTemperature: "Temperature"
61 | maximumTemperature: "Max. temp"
62 | minimumTemperature: "Min. temp"
63 | humidity: "Humidity"
64 | wind: "Wind"
65 | snow: "Snow"
66 | snow_possible: "Snow possible"
67 | rain: "Rain"
68 | rain_possible: "Rain possible"
69 | icerain: "Ice rain"
70 | rime: "Rime"
71 | rime_possible: "Rime"
72 | clear: "Clear"
73 | updateDate: "Updated at"
74 |
75 | ru:
76 | currentTemperature: "Температура"
77 | maximumTemperature: "Макс. темп"
78 | minimumTemperature: "Мин. темп"
79 | humidity: "Влажность"
80 | wind: "Ветер"
81 | snow: "Снег"
82 | snow_possible: "Возможен снег"
83 | rain: "Дождь"
84 | rain_possible: "Возможен дождь"
85 | icerain: "Ледяной дождь"
86 | rime: "Гололед"
87 | rime_possible: "Возможен гололед"
88 | clear: "Ясно"
89 | updateDate: "Дата обновления"
90 |
91 | includes: L.Mixin.Events
92 |
93 | initialize: (@options = {})->
94 | @layer = new L.LayerGroup()
95 | @sourceUrl = "http://openweathermap.org/data/getrect?type={type}&lat1={minlat}&lat2={maxlat}&lng1={minlon}&lng2={maxlon}"
96 | @sourceRequests = {}
97 |
98 | @clusterWidth = @options.clusterWidth or 150
99 | @clusterHeight = @options.clusterHeight or 150
100 | @unitFormatter = UnitFormatters[@options.units or 'metric']
101 |
102 | @type = @options.type or 'city'
103 | @i18n = @options.i18n or @defaultI18n[@options.lang or 'en']
104 | @stationsIcon = if @options.hasOwnProperty('stationsIcon') then @options.stationsIcon else true
105 | @temperatureDigits = @options.temperatureDigits
106 | @temperatureDigits = 2 unless @temperatureDigits?
107 |
108 | Layer.Utils.checkSunCal()
109 |
110 | onAdd: (map) ->
111 | @map = map
112 | @map.addLayer(@layer)
113 | @map.on('moveend', @update, @)
114 |
115 | @update()
116 |
117 | onRemove: (map) ->
118 | return unless @map == map
119 |
120 | @map.off('moveend', @update, @)
121 | @map.removeLayer(@layer)
122 | @map = undefined
123 |
124 | getAttribution: ->
125 | 'Weather data provided by OpenWeatherMap.'
126 |
127 | update: ->
128 | for url, req of @sourceRequests
129 | req.abort()
130 |
131 | @sourceRequests = {}
132 |
133 | @updateType @type
134 |
135 | updateType: (type) ->
136 | bounds = @map.getBounds()
137 | sw = bounds.getSouthWest()
138 | ne = bounds.getNorthEast()
139 |
140 | url = @sourceUrl
141 | .replace('{type}', type)
142 | .replace('{minlat}', sw.lat)
143 | .replace('{maxlat}', ne.lat)
144 | .replace('{minlon}', sw.lng)
145 | .replace('{maxlon}', ne.lng)
146 |
147 | @sourceRequests[type] = Layer.Utils.requestJsonp url, (data) =>
148 | delete @sourceRequests[type]
149 |
150 | @map.removeLayer(@layer)
151 | @layer.clearLayers()
152 |
153 | cells = {}
154 |
155 | for st in data.list
156 | ll = new L.LatLng(st.lat, st.lng)
157 | p = @map.latLngToLayerPoint(ll)
158 | key = "#{Math.round(p.x / @clusterWidth)}_#{Math.round(p.y / @clusterHeight)}"
159 | cells[key] = st if not cells[key] or parseInt(cells[key].rang) < parseInt(st.rang)
160 |
161 | for key, st of cells
162 | @layer.addLayer(@buildMarker(st, new L.LatLng(st.lat, st.lng)))
163 |
164 | @map.addLayer(@layer)
165 |
166 | buildMarker: (st, ll) ->
167 | weatherText = @weatherText(st)
168 | weatherIcon = @weatherIcon(st)
169 |
170 | popupContent = ""
171 | popupContent += "

"
172 | popupContent += "
"
173 | popupContent += "
#{weatherText}
"
174 | popupContent += "
"
175 | popupContent += "#{@i18n.currentTemperature}: #{@unitFormatter.temperature(st.temp, @temperatureDigits)}
"
176 | popupContent += "#{@i18n.maximumTemperature}: #{@unitFormatter.temperature(st.temp_max, @temperatureDigits)}
" if st.temp_max
177 | popupContent += "#{@i18n.minimumTemperature}: #{@unitFormatter.temperature(st.temp_min, @temperatureDigits)}
" if st.temp_min
178 | popupContent += "#{@i18n.humidity}: #{st.humidity}
" if st.humidity
179 | popupContent += "#{@i18n.wind}: #{@unitFormatter.speed(st.wind_ms)}
"
180 | popupContent += "#{@i18n.updateDate}: #{@formatTimestamp(st.dt)}
" if st.dt
181 | popupContent += "
"
182 | popupContent += "
"
183 |
184 | typeIcon = @typeIcon(st)
185 |
186 | markerIcon = if @stationsIcon and typeIcon
187 | new Icon image: typeIcon, text: "#{@unitFormatter.temperature(st.temp, @temperatureDigits)}", textOffset: 30
188 | else
189 | new Icon image: weatherIcon, text: "#{@unitFormatter.temperature(st.temp, @temperatureDigits)}", textOffset: 45
190 |
191 | marker = new L.Marker ll, icon: markerIcon
192 | marker.bindPopup(popupContent)
193 | marker
194 |
195 | formatTimestamp: (ts) ->
196 | date = new Date(ts * 1000)
197 |
198 | m = date.getMonth()+1
199 | m = '0' + m if m < 10
200 |
201 | d = date.getDate()
202 | d = '0' + d if d < 10
203 |
204 | hh = date.getHours()
205 |
206 | mm = date.getMinutes()
207 | mm = '0' + mm if mm < 10
208 |
209 | "#{date.getFullYear()}-#{m}-#{d} #{hh}:#{mm}"
210 |
211 | buildUrl: (st) ->
212 | if st.datatype == 'station'
213 | "http://openweathermap.org/station/#{st.id}"
214 | else
215 | "http://openweathermap.org/city/#{st.id}"
216 |
217 | weatherIcon: (st) ->
218 | day = @dayTime(st)
219 | cl = st.cloud
220 | img = 'transparent'
221 |
222 | if cl < 25 and cl >= 0
223 | img = '01' + day
224 | if cl < 50 and cl >= 25
225 | img = '02' + day
226 | if cl < 75 and cl >= 50
227 | img = '03' + day
228 | if cl >= 75
229 | img = '04'
230 |
231 | if st.prsp_type == '1' and st.prcp > 0
232 | img = '13'
233 |
234 | if st.prsp_type == '4' and st.prcp > 0
235 | img = '09'
236 |
237 | for i in ['23','24','26','27','28','29','33','38','42']
238 | if st.prsp_type == i
239 | img = '09'
240 |
241 | "http://openweathermap.org/images/icons60/#{img}.png"
242 |
243 | typeIcon: (st) ->
244 | if st.datatype == 'station'
245 | if st.type == '1'
246 | "http://openweathermap.org/images/list-icon-3.png"
247 | else if st.type == '2'
248 | "http://openweathermap.org/images/list-icon-2.png"
249 |
250 | weatherText: (st) ->
251 |
252 | if st.prsp_type == '1'
253 | if st.prcp!=0 and st.prcp > 0
254 | "#{@i18n.snow} (#{@unitFormatter.height(st.prcp)})"
255 | else
256 | @i18n.snow_possible
257 | else if st.prsp_type == '2'
258 | if st.prcp!=0 and st.prcp > 0
259 | "#{@i18n.rime} (#{@unitFormatter.height(st.prcp)})"
260 | else
261 | @i18n.rime_possible
262 | else if st.prsp_type == '3'
263 | @i18n.icerain
264 | else if st.prsp_type == '4'
265 | if st.prcp!=0 and st.prcp > 0
266 | "#{@i18n.rain} (#{@unitFormatter.height(st.prcp)})"
267 | else
268 | @i18n.rain_possible
269 | else
270 | @i18n.clear
271 |
272 | dayTime: (st) ->
273 | return 'd' unless SunCalc?
274 |
275 | dt = new Date()
276 | times = SunCalc.getTimes(dt, st.lat, st.lng)
277 | if dt > times.sunrise && dt < times.sunset
278 | 'd'
279 | else
280 | 'n'
281 |
282 | Layer.Utils =
283 | callbacks: {}
284 | callbackCounter: 0
285 |
286 | checkSunCal: ->
287 | return if SunCalc?
288 |
289 | el = document.createElement('script')
290 | el.src = 'https://raw.github.com/mourner/suncalc/master/suncalc.js'
291 | el.type = 'text/javascript'
292 | document.getElementsByTagName('body')[0].appendChild(el)
293 |
294 | requestJsonp: (url, cb) ->
295 | el = document.createElement('script')
296 | counter = (@callbackCounter += 1)
297 | callback = "OsmJs.Weather.LeafletLayer.Utils.callbacks[#{counter}]"
298 |
299 | abort = ->
300 | el.parentNode.removeChild(el) if el.parentNode
301 |
302 | @callbacks[counter] = (data) =>
303 | # el.parentNode.removeChild(el) if el.parentNode
304 | delete @callbacks[counter]
305 | cb(data)
306 |
307 | delim = if url.indexOf('?') >= 0
308 | '&'
309 | else
310 | '?'
311 |
312 | el.src = "#{url}#{delim}callback=#{callback}"
313 | el.type = 'text/javascript'
314 | document.getElementsByTagName('body')[0].appendChild(el)
315 |
316 | {abort: abort}
317 |
318 | @OsmJs = {} unless @OsmJs
319 | @OsmJs.Weather = {} unless @OsmJs.Weather
320 | @OsmJs.Weather.LeafletLayer = Layer
321 |
--------------------------------------------------------------------------------
/dist/validators/leaflet-layer.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var Layer, intersects, intersectsKeys;
4 |
5 | intersects = function(arr1, arr2) {
6 | var el, _i, _len;
7 | for (_i = 0, _len = arr1.length; _i < _len; _i++) {
8 | el = arr1[_i];
9 | if (arr2.indexOf(el) >= 0) {
10 | return true;
11 | }
12 | }
13 | return false;
14 | };
15 |
16 | intersectsKeys = function(arr, hash) {
17 | var el, _i, _len;
18 | for (_i = 0, _len = arr.length; _i < _len; _i++) {
19 | el = arr[_i];
20 | if (hash[el]) {
21 | return true;
22 | }
23 | }
24 | return false;
25 | };
26 |
27 | Layer = L.Class.extend({
28 | includes: L.Mixin.Events,
29 | initialize: function(options) {
30 | var source, _i, _len, _ref, _results;
31 | this.options = options != null ? options : {};
32 | this.sources = {};
33 | this.sourceLayers = {};
34 | this.sourceRequests = {};
35 | this.disabledErrors = [];
36 | this.i18n = this.options.i18n || {
37 | error_info: 'More error info',
38 | errors: 'Errors',
39 | objects: 'Objects',
40 | params: 'Params',
41 | edit_in_potlatch: 'Edit in Potlatch',
42 | edit_in_josm: 'Edit in JOSM',
43 | created_at: 'Created at',
44 | updated_at: 'Updated at'
45 | };
46 | _ref = this.options.sources || [];
47 | _results = [];
48 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
49 | source = _ref[_i];
50 | _results.push(this.addSource(source));
51 | }
52 | return _results;
53 | },
54 | disableError: function(error) {
55 | if (this.disabledErrors.indexOf(error) < 0) {
56 | this.disabledErrors.push(error);
57 | return this.update();
58 | }
59 | },
60 | enableError: function(error) {
61 | var idx;
62 | if ((idx = this.disabledErrors.indexOf(error)) >= 0) {
63 | this.disabledErrors.splice(idx, 1);
64 | return this.update();
65 | }
66 | },
67 | addSource: function(source) {
68 | if (this.sources[source.url]) {
69 | this.sources[source.url] = source;
70 | if (this.map) {
71 | this.updateSource(source);
72 | }
73 | return this.fire('sourcechange', {
74 | source: source
75 | });
76 | } else {
77 | this.sourceLayers[source.url] = new L.LayerGroup();
78 | this.sources[source.url] = source;
79 | if (this.sourceRequests[source.url]) {
80 | this.sourceRequests[source.url].abort();
81 | delete this.sourceRequests[source.url];
82 | }
83 | if (this.map) {
84 | this.map.addLayer(this.sourceLayers[source.url]);
85 | this.updateSource(source);
86 | }
87 | return this.fire('sourceadd', {
88 | source: source
89 | });
90 | }
91 | },
92 | removeSource: function(source) {
93 | if (this.sources[source.url]) {
94 | if (this.map) {
95 | this.map.removeLayer(this.sourceLayers[source.url]);
96 | }
97 | delete this.sourceLayers[source.url];
98 | delete this.sources[source.url];
99 | if (this.sourceRequests[source.url]) {
100 | this.sourceRequests[source.url].abort();
101 | delete this.sourceRequests[source.url];
102 | }
103 | return this.fire('sourceremove', {
104 | source: source
105 | });
106 | }
107 | },
108 | onAdd: function(map) {
109 | var key, layer, _ref;
110 | this.map = map;
111 | _ref = this.sourceLayers;
112 | for (key in _ref) {
113 | layer = _ref[key];
114 | map.addLayer(layer);
115 | }
116 | map.on('moveend', this.update, this);
117 | return this.update();
118 | },
119 | onRemove: function(map) {
120 | var key, layer, _ref;
121 | map.off('moveend', this.update, this);
122 | _ref = this.sourceLayers;
123 | for (key in _ref) {
124 | layer = _ref[key];
125 | map.removeLayer(layer);
126 | }
127 | return this.map = void 0;
128 | },
129 | update: function() {
130 | var req, source, url, _ref, _ref1, _results;
131 | if (!this.map) {
132 | return;
133 | }
134 | _ref = this.sourceRequests;
135 | for (url in _ref) {
136 | req = _ref[url];
137 | req.abort();
138 | }
139 | this.sourceRequests = {};
140 | _ref1 = this.sources;
141 | _results = [];
142 | for (url in _ref1) {
143 | source = _ref1[url];
144 | _results.push(this.updateSource(source));
145 | }
146 | return _results;
147 | },
148 | updateSource: function(source) {
149 | var bounds, ne, sw, url,
150 | _this = this;
151 | bounds = this.map.getBounds();
152 | sw = bounds.getSouthWest();
153 | ne = bounds.getNorthEast();
154 | url = source.url.replace('{minlat}', sw.lat).replace('{maxlat}', ne.lat).replace('{minlon}', sw.lng).replace('{maxlon}', ne.lng).replace('{filtered_types}', this.getErrorTypes(source).join(','));
155 | return this.sourceRequests[source.url] = Layer.Utils.request(url, source, function(data) {
156 | var layer, res, _i, _j, _len, _len1, _ref, _ref1;
157 | delete _this.sourceRequests[source.url];
158 | layer = _this.sourceLayers[source.url];
159 | _this.map.removeLayer(layer);
160 | _ref = data.results;
161 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
162 | res = _ref[_i];
163 | if (res.type) {
164 | res.types = [res.type];
165 | }
166 | }
167 | layer.clearLayers();
168 | _ref1 = data.results;
169 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
170 | res = _ref1[_j];
171 | if (intersectsKeys(res.types, source.types) && !intersects(res.types, _this.disabledErrors)) {
172 | layer.addLayer(_this.buildResult(source, res));
173 | }
174 | }
175 | return _this.map.addLayer(layer);
176 | });
177 | },
178 | getErrorTypes: function(source) {
179 | var desc, type, _ref, _results;
180 | _ref = source.types;
181 | _results = [];
182 | for (type in _ref) {
183 | desc = _ref[type];
184 | if (this.disabledErrors.indexOf(type) < 0) {
185 | _results.push(type);
186 | }
187 | }
188 | return _results;
189 | },
190 | buildResult: function(source, res) {
191 | var bounds, center, errorData, errorTemplate, key, ne, obj, popupText, resLayer, sw, type, value, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3;
192 | bounds = new L.LatLngBounds();
193 | resLayer = new L.GeoJSON({
194 | type: 'Feature',
195 | geometry: res.geometry
196 | });
197 | Layer.Utils.extendBounds(bounds, resLayer);
198 | center = bounds.getCenter();
199 | sw = bounds.getSouthWest();
200 | ne = bounds.getNorthEast();
201 | popupText = "";
202 | popupText += "
" + this.i18n.errors + "
";
203 | popupText += "
";
204 | _ref = res.types;
205 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
206 | type = _ref[_i];
207 | if (!((_ref1 = source.types[type]) != null ? _ref1.text : void 0)) {
208 | continue;
209 | }
210 | errorTemplate = source.types[type].text;
211 | errorData = res.params || {};
212 | popupText += "- ";
213 | popupText += errorTemplate.replace(/\{ *([\w_]+) *\}/g, function(str, key) {
214 | return errorData[key];
215 | });
216 | popupText += "
";
217 | }
218 | popupText += "
";
219 | if (this.options.dateFormat) {
220 | popupText += "
";
221 | if (res.created_at) {
222 | popupText += "" + this.i18n.created_at + ": " + (this.formatDate(new Date(res.created_at))) + "
";
223 | }
224 | if (res.updated_at) {
225 | popupText += "" + this.i18n.updated_at + ": " + (this.formatDate(new Date(res.updated_at))) + "
";
226 | }
227 | popupText += "
";
228 | }
229 | popupText += "
";
230 | if (res.url) {
231 | popupText += "" + this.i18n.error_info + "
";
232 | }
233 | popupText += "" + this.i18n.edit_in_josm + "
";
234 | popupText += "" + this.i18n.edit_in_potlatch + "
";
235 | popupText += "
";
236 | if (res.objects) {
237 | popupText += "
" + this.i18n.objects + "
";
238 | popupText += "
";
239 | _ref2 = res.objects;
240 | for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
241 | obj = _ref2[_j];
242 | popupText += "- " + (obj.join('-')) + "
";
243 | }
244 | popupText += "
";
245 | }
246 | if (res.params) {
247 | popupText += "
" + this.i18n.params + "
";
248 | popupText += "
";
249 | _ref3 = res.params;
250 | for (key in _ref3) {
251 | value = _ref3[key];
252 | popupText += "- " + key + ": " + value + "
";
253 | }
254 | popupText += "
";
255 | }
256 | popupText += "
";
257 | resLayer.bindPopup(popupText);
258 | return resLayer;
259 | },
260 | formatDate: function(date) {
261 | return this.options.dateFormat.replace("DD", (date.getDate() < 10 ? '0' : '') + date.getDate()).replace("MM", (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1)).replace("YYYY", date.getFullYear());
262 | }
263 | });
264 |
265 | Layer.Utils = {
266 | callbacks: {},
267 | callbackCounter: 0,
268 | extendBounds: function(bounds, l) {
269 | if (l.getBounds) {
270 | bounds.extend(l.getBounds().getSouthWest());
271 | return bounds.extend(l.getBounds().getNorthEast());
272 | } else if (l.getLatLng) {
273 | return bounds.extend(l.getLatLng());
274 | } else if (l._iterateLayers) {
275 | return l._iterateLayers(function(c) {
276 | return Layer.Utils.extendBounds(bounds, c);
277 | });
278 | } else {
279 | return console.log(["Can't determine layer bounds", l]);
280 | }
281 | },
282 | request: function(url, source, cb) {
283 | if (source.jsonp) {
284 | return this.requestJsonp(url, cb);
285 | } else {
286 | return this.requestXhr(url, cb);
287 | }
288 | },
289 | requestXhr: function(url, cb) {
290 | var xhr;
291 | xhr = new XMLHttpRequest();
292 | xhr.open('GET', url, true);
293 | xhr.onreadystatechange = function() {
294 | if (xhr.readyState === 4) {
295 | if (xhr.status === 200) {
296 | return cb(eval("(" + xhr.responseText + ")"));
297 | }
298 | }
299 | };
300 | xhr.send();
301 | return xhr;
302 | },
303 | requestJsonp: function(url, cb) {
304 | var abort, callback, counter, delim, el,
305 | _this = this;
306 | el = document.createElement('script');
307 | counter = (this.callbackCounter += 1);
308 | callback = "OsmJs.Validators.LeafletLayer.Utils.callbacks[" + counter + "]";
309 | abort = function() {
310 | if (el.parentNode) {
311 | return el.parentNode.removeChild(el);
312 | }
313 | };
314 | this.callbacks[counter] = function(data) {
315 | if (el.parentNode) {
316 | el.parentNode.removeChild(el);
317 | }
318 | delete _this.callbacks[counter];
319 | return cb(data);
320 | };
321 | delim = url.indexOf('?') >= 0 ? '&' : '?';
322 | el.src = "" + url + delim + "callback=" + callback;
323 | document.getElementsByTagName('body')[0].appendChild(el);
324 | return {
325 | abort: abort
326 | };
327 | }
328 | };
329 |
330 | if (!this.OsmJs) {
331 | this.OsmJs = {};
332 | }
333 |
334 | if (!this.OsmJs.Validators) {
335 | this.OsmJs.Validators = {};
336 | }
337 |
338 | this.OsmJs.Validators.LeafletLayer = Layer;
339 |
340 | }).call(this);
341 |
--------------------------------------------------------------------------------
/dist/weather/leaflet-layer.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.3.1
2 | (function() {
3 | var Icon, Layer, UnitFormatters;
4 |
5 | Icon = L.Icon.extend({
6 | options: {
7 | popupAnchor: new L.Point(0, -25)
8 | },
9 | initialize: function(options) {
10 | return L.Util.setOptions(this, options);
11 | },
12 | createIcon: function() {
13 | var div, span;
14 | div = document.createElement('div');
15 | div.className = 'leaflet-marker-icon weather-icon';
16 | div.style['margin'] = '-30px 0px 0px -30px';
17 | div.style['width'] = '60px';
18 | div.style['height'] = '20px';
19 | div.style['padding'] = "" + this.options.textOffset + "px 0px 0px 0px";
20 | div.style['background'] = "url(" + this.options.image + ") no-repeat center top";
21 | div.style['textAlign'] = 'center';
22 | span = document.createElement('span');
23 | span.innerHTML = this.options.text;
24 | div.appendChild(span);
25 | return div;
26 | },
27 | createShadow: function() {
28 | return null;
29 | }
30 | });
31 |
32 | UnitFormatters = {
33 | metric: {
34 | temperature: function(k, digits) {
35 | var c, p;
36 | p = Math.pow(10, digits);
37 | c = k - 273.15;
38 | return "" + (Math.round(c * p) / p) + " °C";
39 | },
40 | speed: function(v) {
41 | return "" + v + " m/s";
42 | },
43 | height: function(v) {
44 | return "" + v + " mm";
45 | }
46 | },
47 | imperial: {
48 | temperature: function(k, digits) {
49 | var f, p;
50 | p = Math.pow(10, digits);
51 | f = (k - 273.15) * 1.8 + 32;
52 | return "" + (Math.round(f * p) / p) + " °F";
53 | },
54 | speed: function(v) {
55 | v = Math.round(v * 2.237);
56 | return "" + v + " mph";
57 | },
58 | height: function(v) {
59 | v = Math.round(v / 1.27) / 20;
60 | return "" + v + " in";
61 | }
62 | }
63 | };
64 |
65 | Layer = L.Class.extend({
66 | defaultI18n: {
67 | en: {
68 | currentTemperature: "Temperature",
69 | maximumTemperature: "Max. temp",
70 | minimumTemperature: "Min. temp",
71 | humidity: "Humidity",
72 | wind: "Wind",
73 | snow: "Snow",
74 | snow_possible: "Snow possible",
75 | rain: "Rain",
76 | rain_possible: "Rain possible",
77 | icerain: "Ice rain",
78 | rime: "Rime",
79 | rime_possible: "Rime",
80 | clear: "Clear",
81 | updateDate: "Updated at"
82 | },
83 | ru: {
84 | currentTemperature: "Температура",
85 | maximumTemperature: "Макс. темп",
86 | minimumTemperature: "Мин. темп",
87 | humidity: "Влажность",
88 | wind: "Ветер",
89 | snow: "Снег",
90 | snow_possible: "Возможен снег",
91 | rain: "Дождь",
92 | rain_possible: "Возможен дождь",
93 | icerain: "Ледяной дождь",
94 | rime: "Гололед",
95 | rime_possible: "Возможен гололед",
96 | clear: "Ясно",
97 | updateDate: "Дата обновления"
98 | }
99 | },
100 | includes: L.Mixin.Events,
101 | initialize: function(options) {
102 | this.options = options != null ? options : {};
103 | this.layer = new L.LayerGroup();
104 | this.sourceUrl = "http://openweathermap.org/data/getrect?type={type}&lat1={minlat}&lat2={maxlat}&lng1={minlon}&lng2={maxlon}";
105 | this.sourceRequests = {};
106 | this.clusterWidth = this.options.clusterWidth || 150;
107 | this.clusterHeight = this.options.clusterHeight || 150;
108 | this.unitFormatter = UnitFormatters[this.options.units || 'metric'];
109 | this.type = this.options.type || 'city';
110 | this.i18n = this.options.i18n || this.defaultI18n[this.options.lang || 'en'];
111 | this.stationsIcon = this.options.hasOwnProperty('stationsIcon') ? this.options.stationsIcon : true;
112 | this.temperatureDigits = this.options.temperatureDigits;
113 | if (this.temperatureDigits == null) {
114 | this.temperatureDigits = 2;
115 | }
116 | return Layer.Utils.checkSunCal();
117 | },
118 | onAdd: function(map) {
119 | this.map = map;
120 | this.map.addLayer(this.layer);
121 | this.map.on('moveend', this.update, this);
122 | return this.update();
123 | },
124 | onRemove: function(map) {
125 | if (this.map !== map) {
126 | return;
127 | }
128 | this.map.off('moveend', this.update, this);
129 | this.map.removeLayer(this.layer);
130 | return this.map = void 0;
131 | },
132 | getAttribution: function() {
133 | return 'Weather data provided by OpenWeatherMap.';
134 | },
135 | update: function() {
136 | var req, url, _ref;
137 | _ref = this.sourceRequests;
138 | for (url in _ref) {
139 | req = _ref[url];
140 | req.abort();
141 | }
142 | this.sourceRequests = {};
143 | return this.updateType(this.type);
144 | },
145 | updateType: function(type) {
146 | var bounds, ne, sw, url,
147 | _this = this;
148 | bounds = this.map.getBounds();
149 | sw = bounds.getSouthWest();
150 | ne = bounds.getNorthEast();
151 | url = this.sourceUrl.replace('{type}', type).replace('{minlat}', sw.lat).replace('{maxlat}', ne.lat).replace('{minlon}', sw.lng).replace('{maxlon}', ne.lng);
152 | return this.sourceRequests[type] = Layer.Utils.requestJsonp(url, function(data) {
153 | var cells, key, ll, p, st, _i, _len, _ref;
154 | delete _this.sourceRequests[type];
155 | _this.map.removeLayer(_this.layer);
156 | _this.layer.clearLayers();
157 | cells = {};
158 | _ref = data.list;
159 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
160 | st = _ref[_i];
161 | ll = new L.LatLng(st.lat, st.lng);
162 | p = _this.map.latLngToLayerPoint(ll);
163 | key = "" + (Math.round(p.x / _this.clusterWidth)) + "_" + (Math.round(p.y / _this.clusterHeight));
164 | if (!cells[key] || parseInt(cells[key].rang) < parseInt(st.rang)) {
165 | cells[key] = st;
166 | }
167 | }
168 | for (key in cells) {
169 | st = cells[key];
170 | _this.layer.addLayer(_this.buildMarker(st, new L.LatLng(st.lat, st.lng)));
171 | }
172 | return _this.map.addLayer(_this.layer);
173 | });
174 | },
175 | buildMarker: function(st, ll) {
176 | var marker, markerIcon, popupContent, typeIcon, weatherIcon, weatherText;
177 | weatherText = this.weatherText(st);
178 | weatherIcon = this.weatherIcon(st);
179 | popupContent = "";
180 | popupContent += "

";
181 | popupContent += "
";
182 | popupContent += "
" + weatherText + "
";
183 | popupContent += "
";
184 | popupContent += "" + this.i18n.currentTemperature + ": " + (this.unitFormatter.temperature(st.temp, this.temperatureDigits)) + "
";
185 | if (st.temp_max) {
186 | popupContent += "" + this.i18n.maximumTemperature + ": " + (this.unitFormatter.temperature(st.temp_max, this.temperatureDigits)) + "
";
187 | }
188 | if (st.temp_min) {
189 | popupContent += "" + this.i18n.minimumTemperature + ": " + (this.unitFormatter.temperature(st.temp_min, this.temperatureDigits)) + "
";
190 | }
191 | if (st.humidity) {
192 | popupContent += "" + this.i18n.humidity + ": " + st.humidity + "
";
193 | }
194 | popupContent += "" + this.i18n.wind + ": " + (this.unitFormatter.speed(st.wind_ms)) + "
";
195 | if (st.dt) {
196 | popupContent += "" + this.i18n.updateDate + ": " + (this.formatTimestamp(st.dt)) + "
";
197 | }
198 | popupContent += "
";
199 | popupContent += "
";
200 | typeIcon = this.typeIcon(st);
201 | markerIcon = this.stationsIcon && typeIcon ? new Icon({
202 | image: typeIcon,
203 | text: "" + (this.unitFormatter.temperature(st.temp, this.temperatureDigits)),
204 | textOffset: 30
205 | }) : new Icon({
206 | image: weatherIcon,
207 | text: "" + (this.unitFormatter.temperature(st.temp, this.temperatureDigits)),
208 | textOffset: 45
209 | });
210 | marker = new L.Marker(ll, {
211 | icon: markerIcon
212 | });
213 | marker.bindPopup(popupContent);
214 | return marker;
215 | },
216 | formatTimestamp: function(ts) {
217 | var d, date, hh, m, mm;
218 | date = new Date(ts * 1000);
219 | m = date.getMonth() + 1;
220 | if (m < 10) {
221 | m = '0' + m;
222 | }
223 | d = date.getDate();
224 | if (d < 10) {
225 | d = '0' + d;
226 | }
227 | hh = date.getHours();
228 | mm = date.getMinutes();
229 | if (mm < 10) {
230 | mm = '0' + mm;
231 | }
232 | return "" + (date.getFullYear()) + "-" + m + "-" + d + " " + hh + ":" + mm;
233 | },
234 | buildUrl: function(st) {
235 | if (st.datatype === 'station') {
236 | return "http://openweathermap.org/station/" + st.id;
237 | } else {
238 | return "http://openweathermap.org/city/" + st.id;
239 | }
240 | },
241 | weatherIcon: function(st) {
242 | var cl, day, i, img, _i, _len, _ref;
243 | day = this.dayTime(st);
244 | cl = st.cloud;
245 | img = 'transparent';
246 | if (cl < 25 && cl >= 0) {
247 | img = '01' + day;
248 | }
249 | if (cl < 50 && cl >= 25) {
250 | img = '02' + day;
251 | }
252 | if (cl < 75 && cl >= 50) {
253 | img = '03' + day;
254 | }
255 | if (cl >= 75) {
256 | img = '04';
257 | }
258 | if (st.prsp_type === '1' && st.prcp > 0) {
259 | img = '13';
260 | }
261 | if (st.prsp_type === '4' && st.prcp > 0) {
262 | img = '09';
263 | }
264 | _ref = ['23', '24', '26', '27', '28', '29', '33', '38', '42'];
265 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
266 | i = _ref[_i];
267 | if (st.prsp_type === i) {
268 | img = '09';
269 | }
270 | }
271 | return "http://openweathermap.org/images/icons60/" + img + ".png";
272 | },
273 | typeIcon: function(st) {
274 | if (st.datatype === 'station') {
275 | if (st.type === '1') {
276 | return "http://openweathermap.org/images/list-icon-3.png";
277 | } else if (st.type === '2') {
278 | return "http://openweathermap.org/images/list-icon-2.png";
279 | }
280 | }
281 | },
282 | weatherText: function(st) {
283 | if (st.prsp_type === '1') {
284 | if (st.prcp !== 0 && st.prcp > 0) {
285 | return "" + this.i18n.snow + " (" + (this.unitFormatter.height(st.prcp)) + ")";
286 | } else {
287 | return this.i18n.snow_possible;
288 | }
289 | } else if (st.prsp_type === '2') {
290 | if (st.prcp !== 0 && st.prcp > 0) {
291 | return "" + this.i18n.rime + " (" + (this.unitFormatter.height(st.prcp)) + ")";
292 | } else {
293 | return this.i18n.rime_possible;
294 | }
295 | } else if (st.prsp_type === '3') {
296 | return this.i18n.icerain;
297 | } else if (st.prsp_type === '4') {
298 | if (st.prcp !== 0 && st.prcp > 0) {
299 | return "" + this.i18n.rain + " (" + (this.unitFormatter.height(st.prcp)) + ")";
300 | } else {
301 | return this.i18n.rain_possible;
302 | }
303 | } else {
304 | return this.i18n.clear;
305 | }
306 | },
307 | dayTime: function(st) {
308 | var dt, times;
309 | if (typeof SunCalc === "undefined" || SunCalc === null) {
310 | return 'd';
311 | }
312 | dt = new Date();
313 | times = SunCalc.getTimes(dt, st.lat, st.lng);
314 | if (dt > times.sunrise && dt < times.sunset) {
315 | return 'd';
316 | } else {
317 | return 'n';
318 | }
319 | }
320 | });
321 |
322 | Layer.Utils = {
323 | callbacks: {},
324 | callbackCounter: 0,
325 | checkSunCal: function() {
326 | var el;
327 | if (typeof SunCalc !== "undefined" && SunCalc !== null) {
328 | return;
329 | }
330 | el = document.createElement('script');
331 | el.src = 'https://raw.github.com/mourner/suncalc/master/suncalc.js';
332 | el.type = 'text/javascript';
333 | return document.getElementsByTagName('body')[0].appendChild(el);
334 | },
335 | requestJsonp: function(url, cb) {
336 | var abort, callback, counter, delim, el,
337 | _this = this;
338 | el = document.createElement('script');
339 | counter = (this.callbackCounter += 1);
340 | callback = "OsmJs.Weather.LeafletLayer.Utils.callbacks[" + counter + "]";
341 | abort = function() {
342 | if (el.parentNode) {
343 | return el.parentNode.removeChild(el);
344 | }
345 | };
346 | this.callbacks[counter] = function(data) {
347 | delete _this.callbacks[counter];
348 | return cb(data);
349 | };
350 | delim = url.indexOf('?') >= 0 ? '&' : '?';
351 | el.src = "" + url + delim + "callback=" + callback;
352 | el.type = 'text/javascript';
353 | document.getElementsByTagName('body')[0].appendChild(el);
354 | return {
355 | abort: abort
356 | };
357 | }
358 | };
359 |
360 | if (!this.OsmJs) {
361 | this.OsmJs = {};
362 | }
363 |
364 | if (!this.OsmJs.Weather) {
365 | this.OsmJs.Weather = {};
366 | }
367 |
368 | this.OsmJs.Weather.LeafletLayer = Layer;
369 |
370 | }).call(this);
371 |
--------------------------------------------------------------------------------