added: notes and todos for keepRight

This commit is contained in:
Thomas Hervey
2018-11-18 19:31:10 -08:00
committed by Bryan Housel
parent ee00635cc4
commit cedf6955b5
4 changed files with 314 additions and 196 deletions
+224 -189
View File
@@ -8,253 +8,288 @@ import rbush from 'rbush';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { request as d3_request } from 'd3-request';
import { geoExtent, geoVecAdd } from '../geo';
import { geoExtent } from '../geo';
import { services } from './index';
import { krError } from '../osm';
import {
utilRebind,
utilTiler,
utilQsString
} from '../util';
import { utilRebind, utilTiler, utilQsString } from '../util';
var tiler = utilTiler();
var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedKeepRight');
var dispatch = d3_dispatch(
'authLoading',
'authDone',
'change',
'loading',
'loaded',
'loadedKeepRight'
);
var _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()};
var _keepRightCache = {
loaded: {},
inflight: {},
inflightPost: {},
keepRight: {},
rtree: rbush()
};
var _off;
var _keepRightZoom = 16;
var _keepRightZoom = 14;
var apiBase = 'https://www.keepright.at/';
function abortRequest(i) {
if (i) {
i.abort();
}
if (i) {
i.abort();
}
}
function abortUnwantedRequests(cache, tiles) {
_forEach(cache.inflight, function(v, k) {
var wanted = _find(tiles, function(tile) { return k === tile.id; });
if (!wanted) {
abortRequest(v);
delete cache.inflight[k];
}
});
_forEach(cache.inflight, function(v, k) {
var wanted = _find(tiles, function(tile) {
return k === tile.id;
});
if (!wanted) {
abortRequest(v);
delete cache.inflight[k];
}
});
}
function encodeErrorRtree(error) {
return {
minX: error.loc[0],
minY: error.loc[1],
maxX: error.loc[0],
maxY: error.loc[1],
data: error
};
return {
minX: error.loc[0],
minY: error.loc[1],
maxX: error.loc[0],
maxY: error.loc[1],
data: error
};
}
// replace or remove error from rtree
function updateRtree(item, replace) {
_keepRightCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; });
_keepRightCache.rtree.remove(item, function isEql(a, b) {
return a.data.id === b.data.id;
});
if (replace) {
_keepRightCache.rtree.insert(item);
}
if (replace) {
_keepRightCache.rtree.insert(item);
}
}
export default {
init: function() {
if (!_keepRightCache) {
this.reset();
}
init: function() {
if (!_keepRightCache) {
this.reset();
}
this.event = utilRebind(this, dispatch, 'on');
},
this.event = utilRebind(this, dispatch, 'on');
},
reset: function() {
_forEach(_keepRightCache.inflight, abortRequest);
reset: function() {
_forEach(_keepRightCache.inflight, abortRequest);
_keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()};
},
_keepRightCache = {
loaded: {},
inflight: {},
keepRight: {},
rtree: rbush()
};
},
loadKeepRightErrors: function(context, projection, options, callback) {
options = _extend({ 'format': 'geojson' }, options);
if (_off) return;
loadKeepRightErrors: function(context, projection, options, callback) {
options = _extend({ format: 'geojson' }, options); // set format to geojson
if (_off) return;
var cache = _keepRightCache;
var cache = _keepRightCache;
var that = this;
var path = apiBase +
'export.php?' +
'format=' + options.format +
'&ch=' + options.ch.join() + '&';
// determine the needed tiles to cover the view
var tiles = tiler.zoomExtent([_keepRightZoom, _keepRightZoom]).getTiles(projection);
// NOTE: the KeepRight API doesn't seem to load
var path =
apiBase +
'export.php?' +
'format=' +
options.format +
'&st=' +
options.st +
'&ch=' +
options.ch.join() +
'&';
// abort inflight requests that are no longer needed
var hadRequests = !_isEmpty(cache.inflight);
abortUnwantedRequests(cache, tiles);
if (hadRequests && _isEmpty(cache.inflight)) {
dispatch.call('loaded'); // stop the spinner
}
// determine the needed tiles to cover the view
var tiles = tiler
.zoomExtent([_keepRightZoom, _keepRightZoom])
.getTiles(projection);
// issue new requests..
tiles.forEach(function(tile) {
if (cache.loaded[tile.id] || cache.inflight[tile.id]) return;
if (_isEmpty(cache.inflight)) {
dispatch.call('loading'); // start the spinner
}
// abort inflight requests that are no longer needed
var hadRequests = !_isEmpty(cache.inflight);
abortUnwantedRequests(cache, tiles);
if (hadRequests && _isEmpty(cache.inflight)) {
dispatch.call('loaded'); // stop the spinner
}
var rect = tile.extent.rectangle();
var nextPath = path +
utilQsString({
left: rect[0],
bottom: [3],
right: rect[2],
top: rect[1]
});
// issue new requests..
tiles.forEach(function(tile) {
if (cache.loaded[tile.id] || cache.inflight[tile.id]) return;
if (_isEmpty(cache.inflight)) {
dispatch.call('loading'); // start the spinner
}
var rect = tile.extent.rectangle();
var nextPath =
path +
utilQsString({
left: rect[0],
bottom: [3],
right: rect[2],
top: rect[1]
});
var options = {}; // TODO: implement
var options = {}; // TODO: implement
cache.inflight[tile.id] = that.loadFromAPI(
nextPath,
function(err, data) {
if (err || !data.features || !data.features.length) return;
cache.inflight[tile.id] = that.loadFromAPI(
nextPath,
function(err, data) {
if (err || !data.features || !data.features.length) return;
cache.loaded[tile.id] = true;
delete cache.inflight[tile.id];
cache.loaded[tile.id] = true;
delete cache.inflight[tile.id];
if (callback) {
callback(err, _extend({ data: data }, tile));
}
if (_isEmpty(cache.inflight)) {
dispatch.call('loaded'); // stop the spinner
}
},
options
);
});
},
if (callback) {
callback(err, _extend({ data: data }, tile));
}
if (_isEmpty(cache.inflight)) {
dispatch.call('loaded'); // stop the spinner
}
},
options
);
});
},
loadFromAPI: function(path, callback, options) {
var cache = _keepRightCache;
loadFromAPI: function(path, callback, options) {
var cache = _keepRightCache;
return d3_request(path)
.mimeType('application/json') // TODO: only have this as a response if the input format is json
.header('Content-type', 'application/x-www-form-urlencoded')
.response(function(xhr) {
return JSON.parse(xhr.responseText);
})
.get(function(err, data) {
return d3_request(path)
.mimeType('application/json') // TODO: only have this as a response if the input format is json
.header('Content-type', 'application/x-www-form-urlencoded')
.response(function(xhr) {
return JSON.parse(xhr.responseText);
})
.get(function(err, data) {
var features = data.features
.map(function(feature) {
var loc = feature.geometry.coordinates;
var props = feature.properties;
var features = data.features.map(function(feature) {
var loc = feature.geometry.coordinates;
var props = feature.properties;
// TODO: finish implementing overlapping error offset
// // if errors are coincident, move them apart slightly
// var coincident = false;
// var epsilon = 0.00001;
// do {
// if (coincident) {
// loc = geoVecAdd(loc, [epsilon, epsilon]);
// }
// var bbox = geoExtent(loc).bbox();
// coincident = cache.rtree.search(bbox).length;
// } while (coincident);
// TODO: finish implementing overlapping error offset
// // if errors are coincident, move them apart slightly
// var coincident = false;
// var epsilon = 0.00001;
// do {
// if (coincident) {
// loc = geoVecAdd(loc, [epsilon, epsilon]);
// }
// var bbox = geoExtent(loc).bbox();
// coincident = cache.rtree.search(bbox).length;
// } while (coincident);
var d = new krError({
loc: loc,
id: props.error_id,
comment: props.comment || null,
description: props.description || '',
error_id: props.error_id,
error_type: props.error_type,
object_id: props.object_id,
object_type: props.object_type,
schema: props.schema,
title: props.title
});
var d = new krError ({
loc: loc,
id: props.error_id,
comment: props.comment || null,
description: props.description || '',
error_id: props.error_id,
error_type: props.error_type,
object_id: props.object_id,
object_type: props.object_type,
schema: props.schema,
title: props.title
});
cache.keepRight[d.id] = d;
cache.keepRight[d.id] = d;
return {
minX: loc[0],
minY: loc[1],
maxX: loc[0],
maxY: loc[1],
data: d
};
})
.filter(Boolean);
return {
minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
};
cache.rtree.load(features);
dispatch.call('loadedKeepRight');
}).filter(Boolean);
callback(err, data);
});
},
cache.rtree.load(features);
dispatch.call('loadedKeepRight');
postKeepRightUpdate: function(update, callback) {
if (!services.osm.authenticated()) {
return callback({ message: 'Not Authenticated', status: -3 }, update);
}
if (_keepRightCache.inflightPost[update.id]) {
return callback(
{ message: 'Error update already inflight', status: -2 }, update);
}
callback(err, data);
});
},
var path = apiBase + 'comment.php?';
if (update.state) {
path += '&st=' + update.state;
}
if (update.newComment) {
path += '&' + utilQsString({ co: update.newComment });
}
postKeepRightUpdate: function(d, callback) {
// TODO: check if a user is authenticated
// if (!this.authenticated()) {
// return callback({ message: 'Not Authenticated', status: -3 }, d);
// }
// if (_keepRightCache.inflightPost[d.id]) {
// return callback({ message: 'Error update already inflight', status: -2 }, d);
// }
path += '&schema=' + update.schema + '&id=' + update.error_id;
var path = apiBase + 'comment.php?';
if (d.state) { path += '&st=' + d.state; }
if (d.newComment) { path += '&' + utilQsString({'co': d.newComment }); }
_keepRightCache.inflightPost[update.id] = d3_request(path)
.mimeType('application/json')
.response(function(xhr) {
return JSON.parse(xhr.responseText);
})
.post(function(err, data) {
delete _keepRightCache.inflightPost[update.id];
if (err) { return callback(err); }
path += '&schema=' + d.schema + '&id=' + d.error_id;
console.log('data ', data);
});
d3_request(path)
.mimeType('application/json')
.response(function(xhr) {
return JSON.parse(xhr.responseText);
})
.post(function(err, data) {
console.log('error:', err);
console.log('data: ', data);
});
},
// NOTE: This throws a CORS error, but it seems successful?
},
// get all cached errors covering the viewport
keepRight: function(projection) {
var viewport = projection.clipExtent();
var min = [viewport[0][0], viewport[1][1]];
var max = [viewport[1][0], viewport[0][1]];
var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
// get all cached errors covering the viewport
keepRight: function(projection) {
var viewport = projection.clipExtent();
var min = [viewport[0][0], viewport[1][1]];
var max = [viewport[1][0], viewport[0][1]];
var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
return _keepRightCache.rtree.search(bbox)
.map(function(d) { return d.data; });
},
return _keepRightCache.rtree.search(bbox).map(function(d) {
return d.data;
});
},
// get a single error from the cache
getError: function(id) {
return _keepRightCache.keepRight[id];
},
// get a single error from the cache
getError: function(id) {
return _keepRightCache.keepRight[id];
},
// replace a single error in the cache
replaceError: function(error) {
if (!(error instanceof krError) || !error.id) return;
// replace a single error in the cache
replaceError: function(error) {
if (!(error instanceof krError) || !error.id) return;
_keepRightCache.keepRight[error.id] = error;
updateRtree(encodeErrorRtree(error), true); // true = replace
return error;
},
_keepRightCache.keepRight[error.id] = error;
updateRtree(encodeErrorRtree(error), true); // true = replace
return error;
},
// remove a single error from the cache
removeError: function(error) {
if (!(error instanceof krError) || !error.id) return;
// remove a single error from the cache
removeError: function(error) {
if (!(error instanceof krError) || !error.id) return;
delete _keepRightCache.keepRight[error.id];
updateRtree(encodeErrorRtree(error), false); // false = remove
},
};
delete _keepRightCache.keepRight[error.id];
updateRtree(encodeErrorRtree(error), false); // false = remove
}
};
+1
View File
@@ -185,6 +185,7 @@ export function svgKeepRight(projection, context, dispatch) {
editOn();
update();
var options = {
st: '', // NOTE: passing in 'ignore' or 'ignore_t' seems to have no effect
ch: [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413]
};
+3 -2
View File
@@ -289,7 +289,8 @@ export function uiKeepRightEditor(context) {
this.blur(); // avoid keeping focus on the button - #4641
var keepRight = services.keepRight;
if (keepRight) {
d.state = 'ignore_t';
d.state = d.state === 'ignore_t' ? '' : 'ignore_t';
keepRight.postKeepRightUpdate(d, function(err, error) {
dispatch.call('change', error);
});
@@ -306,7 +307,7 @@ export function uiKeepRightEditor(context) {
this.blur(); // avoid keeping focus on the button - #4641
var keepRight = services.keepRight;
if (keepRight) {
d.state = 'ignore';
d.state = d.state === 'ignore' ? '' : 'ignore';
keepRight.postKeepRightUpdate(d, function(err, error) {
dispatch.call('change', error);
});