├── .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 |
--------------------------------------------------------------------------------