├── .gitignore ├── CHANGELOG.md ├── Makefile ├── site.js ├── index.html ├── package.json ├── LICENSE ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store/ 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.0.12 2 | 3 | - Rollback to path `api/augmented_diff_status` 4 | See _https://github.com/osmlab/osm-stream/issues/5_ 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: bundle.js osmstream.js 2 | 3 | bundle.js: index.js package.json site.js 4 | browserify site.js > bundle.js 5 | 6 | osmstream.js: index.js 7 | browserify index.js -s osmStream > osmstream.js 8 | -------------------------------------------------------------------------------- /site.js: -------------------------------------------------------------------------------- 1 | var osmStream = require('./'); 2 | 3 | var p = document.getElementById('output'); 4 | 5 | osmStream.runFn(function(err, data) { 6 | if (err) p.innerHTML += '\nError'; 7 | p.innerHTML += '\nData: ' + data.length; 8 | }); 9 | 10 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

10 |         
11 |     
12 | 
13 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "osm-stream",
 3 |   "version": "0.0.15",
 4 |   "description": "stream minutely openstreetmap diffs",
 5 |   "main": "index.js",
 6 |   "scripts": {
 7 |     "test": "echo \"Error: no test specified\" && exit 1",
 8 |     "start": "./node_modules/browservefy/bin/browservefy index.js:bundle.js 8080 --browserify=./node_modules/browserify/bin/cmd.js -- -d"
 9 |   },
10 |   "repository": "git@github.com:osmlab/osm-stream.git",
11 |   "keywords": [
12 |     "openstreetmap"
13 |   ],
14 |   "author": "Tom MacWright ",
15 |   "license": "Public Domain",
16 |   "dependencies": {
17 |     "reqwest": "~0.7.3",
18 |     "through": "~2.3.1",
19 |     "qs": "~>1.0.0"
20 |   },
21 |   "devDependencies": {
22 |     "browserify": "2.4.3"
23 |   }
24 | }
25 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | This is free and unencumbered software released into the public domain.
 2 | 
 3 | Anyone is free to copy, modify, publish, use, compile, sell, or
 4 | distribute this software, either in source code form or as a compiled
 5 | binary, for any purpose, commercial or non-commercial, and by any
 6 | means.
 7 | 
 8 | In jurisdictions that recognize copyright laws, the author or authors
 9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 | 
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 | 
24 | For more information, please refer to 
25 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | ## OSM Stream
 2 | 
 3 | Uses the [Overpass API](http://overpass-api.de/) for
 4 | [Augmented Diffs](http://wiki.openstreetmap.org/wiki/Overpass_API/Augmented_Diffs),
 5 | loads data with CORS and exposes a stream.
 6 | 
 7 | ## using
 8 | 
 9 | Without browserify: copy `osmstream.js`. That works as an `osmStream` global
10 | and with UMD.
11 | 
12 | With browserify `npm install osm-stream`
13 | 
14 | ## api
15 | 
16 | `s.once(function(err, data) { }, [bbox])`
17 | 
18 | Get one batch of changes right now.
19 | 
20 | `s.run(function(err, stream), duration, [dir], [bbox], [maxRetries])`
21 | 
22 | duration is how long between runs: default 1 minute
23 | 
24 | dir is direction: either `1`, the default, or `-1` for rewind.
25 | 
26 | maxRetries: How often to retry fetching the current diff before skipping it.
27 | 
28 | `s.runFn(function(err, stream), duration, [dir], [bbox], [maxRetries])`
29 | 
30 | Same as `.run` but instead of returning a stream that pipes objects, calls
31 | the callback once per object.
32 | 
33 | duration is how long between runs: default 1 minute
34 | 
35 | dir is direction: either `1`, the default, or `-1` for rewind.
36 | 
37 | maxRetries: How often to retry fetching the current diff before skipping it.
38 | 
39 | ## example
40 | 
41 | ```js
42 | var osmStream = require('osm-stream');
43 | 
44 | // re-request every 60s
45 | osmStream
46 |     .run(function(err, stream) {
47 |         stream.on('data', function(d) {
48 |             console.log(d);
49 |         });
50 |     });
51 | 
52 | // re-request every 60s
53 | // callback-style interface
54 | osmStream
55 |     .runFn(function(err, data) {
56 |         // ...
57 |     });
58 | 
59 | // one-time request
60 | osmStream
61 |     .once(function(err, d) {
62 |         console.log(d);
63 |     });
64 | ```
65 | 
66 | The stream returned uses [through](https://github.com/dominictarr/through), so
67 | you can end it and that will also stop the run cycle.
68 | 
69 | ### See Also
70 | 
71 | * [osm-stream-process](https://github.com/iandees/osm-stream-process) in Python
72 | 
73 | ### As Seen
74 | 
75 | * [Show Me The Way](http://osmlab.github.io/show-me-the-way/) [source](https://github.com/osmlab/show-me-the-way)
76 | * [OSM Live Map](http://osmlab.github.io/osm-live-map/) [source](https://github.com/osmlab/osm-live-map)
77 | * [OSMBuzz](http://spatialbit.com/osmbuzz/)
78 | 


--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
  1 | var reqwest = require('reqwest'),
  2 |     qs = require('qs'),
  3 |     through = require('through');
  4 | 
  5 | var osmStream = (function osmMinutely() {
  6 |     var s = {};
  7 | 
  8 |     // presets
  9 |     var baseUrl = 'https://overpass-api.de/',
 10 |         minuteStatePath = 'api/augmented_diff_status',
 11 |         changePath = 'api/augmented_diff?';
 12 | 
 13 |     function minuteStateUrl() {
 14 |         return baseUrl + minuteStatePath;
 15 |     }
 16 | 
 17 |     function changeUrl(id, bbox) {
 18 |         return baseUrl + changePath + qs.stringify({
 19 |             id: id, info: 'no', bbox: bbox || '-180,-90,180,90'
 20 |         });
 21 |     }
 22 | 
 23 |     function requestState(cb) {
 24 |         reqwest({
 25 |             url: minuteStateUrl(),
 26 |             crossOrigin: true,
 27 |             type: 'text',
 28 |             success: function(res) {
 29 |                 cb(null, parseInt(res.response, 10));
 30 |             }
 31 |         });
 32 |     }
 33 | 
 34 |     function requestChangeset(state, cb, bbox) {
 35 |         reqwest({
 36 |             url: changeUrl(state, bbox),
 37 |             crossOrigin: true,
 38 |             type: 'xml',
 39 |             success: function(res) {
 40 |                 cb(null, res);
 41 |             },
 42 |             error: function (err) {
 43 |                 cb(err);
 44 |             }
 45 |         });
 46 |     }
 47 | 
 48 |     function parseTags(x) {
 49 |         var tgs = x.getElementsByTagName('tag');
 50 |         var tags = {};
 51 |         for (var j = 0; j < tgs.length; j++) {
 52 |             tags[tgs[j].getAttribute("k")] = tgs[j].getAttribute("v");
 53 |         }
 54 |         return tags;
 55 |     }
 56 | 
 57 |     function parseNodeBase(x) {
 58 |         if (!x) return undefined;
 59 |         return {
 60 |             type: x.tagName,
 61 |             id: +x.getAttribute('id'),
 62 |             version: +x.getAttribute('version'),
 63 |             timestamp: x.getAttribute('timestamp'),
 64 |             changeset: +x.getAttribute('changeset'),
 65 |             uid: +x.getAttribute('uid'),
 66 |             user: x.getAttribute('user'),
 67 |             visible: x.getAttribute('visible') !== 'false',
 68 |             tags: parseTags(x)
 69 |         };
 70 |     }
 71 | 
 72 |     function parseBounds(x) {
 73 |         var bounds = get(x, ['bounds']);
 74 |         return [
 75 |             +bounds.getAttribute('maxlat'),
 76 |             +bounds.getAttribute('maxlon'),
 77 |             +bounds.getAttribute('minlat'),
 78 |             +bounds.getAttribute('minlon')
 79 |         ];
 80 |     }
 81 | 
 82 |     function parseMembers(x) {
 83 |         var mbrs = x.getElementsByTagName('members');
 84 |         var members = [];
 85 | 
 86 |         for (var i = 0; i < mbrs.length; i++) {
 87 |             var mbr = {
 88 |                 type: mbrs[i].getAttribute('type'),
 89 |                 ref: +mbrs[i].getAttribute('ref'),
 90 |                 role: mbrs[i].getAttribute('role')
 91 |             };
 92 | 
 93 |             members.push(mbr);
 94 |         }
 95 | 
 96 |         return members;
 97 |     }
 98 | 
 99 |     function parseLinestring(x) {
100 |         var nds = x.getElementsByTagName('nd');
101 |         var nodes = [];
102 | 
103 |         for (var i = 0; i < nds.length; i++) {
104 |             nodes.push([
105 |                 +nds[i].getAttribute('lat'),
106 |                 +nds[i].getAttribute('lon')
107 |             ]);
108 |         }
109 | 
110 |         return nodes;
111 |     }
112 | 
113 |     function parseNode(x) {
114 |         if (!x) return undefined;
115 |         var o = parseNodeBase(x);
116 |         if (o.type === 'node') {
117 |             o.lat = +x.getAttribute('lat');
118 |             o.lon = +x.getAttribute('lon');
119 |         } else if (o.type === 'way') {
120 |             o.bounds = parseBounds(x);
121 | 
122 |             var nodes = parseLinestring(x);
123 |             if (nodes.length > 0) {
124 |                 o.linestring = nodes;
125 |             }
126 |         } else if (o.type === 'relation') {
127 |             o.bounds = parseBounds(x);
128 |             o.members = parseMembers(x);
129 | 
130 | 
131 |         }
132 |         return o;
133 |     }
134 | 
135 |     function get(x, y) {
136 |         if (!x) return undefined;
137 |         for (var i = 0; i < y.length; i++) {
138 |             var o = x.getElementsByTagName(y[i])[0];
139 |             if (o) return o;
140 |         }
141 |     }
142 | 
143 |     function run(id, cb, bbox) {
144 |         requestChangeset(id, function(err, xml) {
145 |             if (err || !xml) return cb('Error');
146 |             if (!xml.getElementsByTagName) return cb('No items');
147 |             var actions = xml.getElementsByTagName('action'), a;
148 |             var items = [];
149 |             for (var i = 0; i < actions.length; i++) {
150 |                 var o = {};
151 |                 a = actions[i];
152 |                 o.type = a.getAttribute('type');
153 |                 if (o.type == 'create') {
154 |                     o.neu = parseNode(get(a, ['node', 'way']));
155 |                 } else if (o.type == 'modify') {
156 |                     o.old = parseNode(get(get(a, ['old']), ['node', 'way']));
157 |                     o.neu = parseNode(get(get(a, ['new']), ['node', 'way']));
158 |                 } else if (o.type == 'delete') {
159 |                     o.old = parseNode(get(get(a, ['old']), ['node', 'way']));
160 |                     o.neu = parseNodeBase(get(get(a, ['new']), ['node', 'way']));
161 |                 }
162 |                 if (o.old || o.neu) {
163 |                     items.push(o);
164 |                 }
165 |             }
166 |             cb(null, items);
167 |         }, bbox);
168 |     }
169 | 
170 |     s.once = function(cb, bbox) {
171 |         requestState(function(err, state) {
172 |             var stream = through(function write(err, data) {
173 |                 cb(null, data);
174 |             });
175 |             run(state, stream.write, bbox);
176 |         });
177 |     };
178 | 
179 |     s.run = function(cb, duration, dir, bbox, maxRetries) {
180 |         dir = dir || 1;
181 |         duration = duration || 60 * 1000;
182 |         var tries = 0;
183 |         var cancel = false;
184 |         function setCancel() { cancel = true; }
185 |         requestState(function(err, state) {
186 |             var stream = through(
187 |                 function write(data) {
188 |                     this.queue(data);
189 |                 },
190 |                 function end() {
191 |                     cancel = true;
192 |                     this.queue(null);
193 |                 });
194 |             function write(items) {
195 |                 for (var i = 0; i < items.length; i++) {
196 |                     stream.write(items[i]);
197 |                 }
198 |             }
199 |             cb(null, stream);
200 |             function iterate() {
201 |                 run(state, function(err, items) {
202 |                     if (!err) {
203 |                         write(items);
204 |                     }
205 |                     if (!err || ((maxRetries || maxRetries === 0) && tries >= maxRetries)) {
206 |                         tries = 0;
207 |                         state += dir;
208 |                     }
209 |                     else {
210 |                         tries++;
211 |                     }
212 |                     if (!cancel) setTimeout(iterate, duration);
213 |                 }, bbox);
214 |             }
215 |             iterate();
216 |         });
217 |         return { cancel: setCancel };
218 |     };
219 | 
220 |     s.runFn = function(cb, duration, dir, bbox, maxRetries) {
221 |         dir = dir || 1;
222 |         duration = duration || 60 * 1000;
223 |         var tries = 0;
224 |         function setCancel() { cancel = true; }
225 |         var cancel = false;
226 |         requestState(function(err, state) {
227 |             function write(items) { cb(null, items); }
228 |             function iterate() {
229 |                 run(state, function(err, items) {
230 |                     if (!err) {
231 |                         write(items);
232 |                     }
233 |                     if (!err || ((maxRetries || maxRetries === 0) && tries >= maxRetries)) {
234 |                         tries = 0;
235 |                         state += dir;
236 |                     }
237 |                     else {
238 |                         tries++;
239 |                     }
240 |                     if (!cancel) setTimeout(iterate, duration);
241 |                 }, bbox);
242 |             }
243 |             iterate();
244 |         });
245 |         return { cancel: setCancel };
246 |     };
247 | 
248 |     return s;
249 | })();
250 | 
251 | module.exports = osmStream;
252 | 


--------------------------------------------------------------------------------