Merge geocoder into feature search UI

This commit is contained in:
John Firebaugh
2013-07-25 19:24:07 -07:00
parent 7c5cb5b3f1
commit 11e2f1d1fe
12 changed files with 93 additions and 276 deletions
+1 -42
View File
@@ -756,6 +756,7 @@ a:hover .icon.out-link { background-position: -500px -14px;}
width:100%;
}
.geocode-item,
.feature-list-item {
width: 100%;
position: relative;
@@ -1949,48 +1950,6 @@ img.wiki-image {
display:inline-block;
}
/* Geocoder */
.geocode-control {
position: relative;
}
.geocode-control form {
position: absolute;
top: 0;
padding: 5px;
}
.geocode-control input {
width: 100%;
}
.geocode-control div.map-overlay {
position: absolute;
top: 40px;
left: -260px;
border-top: 1px solid #CCC;
z-index: 100;
max-height: 240px;
overflow-y: auto;
padding: 0;
}
.geocode-control div.map-overlay span {
display: inline-block;
border-bottom: 1px solid #CCC;
padding: 5px 10px;
width: 100%;
}
.geocode-control div.map-overlay span.not-found {
line-height: 28px;
width: 100%;
}
.geocode-control a:focus {
text-decoration: underline;
}
/* Geolocator */
.geolocate-control {
+2 -3
View File
@@ -164,9 +164,8 @@ en:
list: "Edits by {users}"
truncated_list: "Edits by {users} and {count} others"
geocoder:
title: Find a place
placeholder: Find a place
no_results: "Couldn't locate a place named '{name}'"
search: Continue search on server...
no_results: No results found
geolocate:
title: Show My Location
inspector:
+2 -3
View File
@@ -205,9 +205,8 @@
"truncated_list": "Edits by {users} and {count} others"
},
"geocoder": {
"title": "Find a place",
"placeholder": "Find a place",
"no_results": "Couldn't locate a place named '{name}'"
"search": "Continue search on server...",
"no_results": "No results found"
},
"geolocate": {
"title": "Show My Location"
-1
View File
@@ -75,7 +75,6 @@
<script src='js/id/ui/background.js'></script>
<script src='js/id/ui/modes.js'></script>
<script src='js/id/ui/contributors.js'></script>
<script src='js/id/ui/geocoder.js'></script>
<script src='js/id/ui/help.js'></script>
<script src='js/id/ui/geolocate.js'></script>
<script src='js/id/ui/notice.js'></script>
+1 -28
View File
@@ -37,33 +37,6 @@ iD.behavior.Hash = function(context) {
}
}
// the hash can declare that the map should select a feature, but it can
// do so before any features are loaded. thus wait for the feature to
// be loaded and then select
function willselect(id, hasMap) {
if (!hasMap) {
context.connection().loadEntity(id, function(error, entity) {
if (entity) {
context.map().zoomTo(entity);
}
});
}
context.map().on('drawn.hash', function() {
if (!context.hasEntity(id)) return;
selectoff();
context.enter(iD.modes.Select(context, [id]));
});
context.on('enter.hash', function() {
if (context.mode().id !== 'browse') selectoff();
});
}
function selectoff() {
context.map().on('drawn.hash', null);
}
function hash() {
context.map()
.on('move.hash', move);
@@ -73,7 +46,7 @@ iD.behavior.Hash = function(context) {
if (location.hash) {
var q = iD.util.stringQs(location.hash.substring(1));
if (q.id) willselect(q.id, q.map);
if (q.id) context.loadEntity(q.id, !q.map);
hashchange();
if (q.map) hash.hadHash = true;
}
+24
View File
@@ -119,6 +119,30 @@ window.iD = function () {
}
};
context.loadEntity = function(id, zoomTo) {
if (zoomTo !== false) {
connection.loadEntity(id, function(error, entity) {
if (entity) {
map.zoomTo(entity);
}
});
}
map.on('drawn.loadEntity', function() {
if (!context.hasEntity(id)) return;
map.on('drawn.loadEntity', null);
context.on('enter.loadEntity', null);
context.enter(iD.modes.Select(context, [id]));
});
context.on('enter.loadEntity', function() {
if (mode.id !== 'browse') {
map.on('drawn.loadEntity', null);
context.on('enter.loadEntity', null);
}
});
};
context.editable = function() {
return map.editable() && mode && mode.id !== 'save';
};
-6
View File
@@ -75,12 +75,6 @@ iD.ui = function(context) {
.attr('class', 'map-control background-control')
.call(iD.ui.Background(context));
if (!context.embed()) {
controls.append('div')
.attr('class', 'map-control geocode-control')
.call(iD.ui.Geocoder(context));
}
controls.append('div')
.attr('class', 'map-control help-control')
.call(iD.ui.Help(context));
+63 -13
View File
@@ -1,4 +1,6 @@
iD.ui.FeatureList = function(context) {
var geocodeResults;
function featureList(selection) {
var header = selection.append('div')
.attr('class', 'header fillL cf');
@@ -14,6 +16,7 @@ iD.ui.FeatureList = function(context) {
}
function inputevent() {
geocodeResults = undefined;
drawList();
}
@@ -61,9 +64,10 @@ iD.ui.FeatureList = function(context) {
var name = iD.util.displayName(entity) || '';
if (name.toLowerCase().indexOf(q) >= 0) {
result.push({
id: entity.id,
entity: entity,
geometry: context.geometry(entity.id),
preset: context.presets().match(entity, graph),
type: context.presets().match(entity, graph).name(),
name: name
});
}
@@ -78,22 +82,56 @@ iD.ui.FeatureList = function(context) {
addEntity(visible[i].__data__);
}
(geocodeResults || []).forEach(function(d) {
result.push({
id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
type: (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' '),
name: d.display_name,
extent: new iD.geo.Extent(
[parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
[parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
})
});
return result;
}
function drawList() {
list.classed('filtered', search.property('value').length);
var value = search.property('value');
list.classed('filtered', value.length);
var geocodeButton = list.selectAll('.geocode-item')
.data([0])
.enter().append('button')
.attr('class', 'geocode-item')
.on('click', geocode);
var label = geocodeButton.append('div')
.attr('class', 'label');
label.append('span')
.attr('class', 'entity-name');
var noResults = geocodeResults && geocodeResults.length === 0;
list.selectAll('.geocode-item')
.style('display', noResults || (value && geocodeResults === undefined) ? 'block' : 'none')
.property('disabled', noResults)
.selectAll('.entity-name')
.text(noResults ? t('geocoder.no_results') : t('geocoder.search'));
var items = list.selectAll('.feature-list-item')
.data(features(), function(d) { return d.entity.id; });
.data(features(), function(d) { return d.id; });
var enter = items.enter().append('button')
var enter = items.enter().insert('button', '.geocode-item')
.attr('class', 'feature-list-item')
.on('mouseover', function(d) { mouseover(d.entity); })
.on('mouseout', function(d) { mouseout(); })
.on('click', function(d) { click(d.entity); });
.on('mouseover', mouseover)
.on('mouseout', mouseout)
.on('click', click);
var label = enter.append('div')
label = enter.append('div')
.attr('class', 'label');
label.append('span')
@@ -101,7 +139,7 @@ iD.ui.FeatureList = function(context) {
label.append('span')
.attr('class', 'entity-type')
.text(function(d) { return d.preset.name(); });
.text(function(d) { return d.type; });
label.append('span')
.attr('class', 'entity-name')
@@ -117,8 +155,8 @@ iD.ui.FeatureList = function(context) {
.remove();
}
function mouseover(entity) {
context.surface().selectAll(iD.util.entityOrMemberSelector([entity.id], context.graph()))
function mouseover(d) {
context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
.classed('hover', true);
}
@@ -127,8 +165,20 @@ iD.ui.FeatureList = function(context) {
.classed('hover', false);
}
function click(entity) {
context.enter(iD.modes.Select(context, [entity.id]));
function click(d) {
if (d.entity) {
context.enter(iD.modes.Select(context, [d.entity.id]));
} else {
context.loadEntity(d.id);
}
}
function geocode() {
var searchVal = encodeURIComponent(search.property('value'));
d3.json('http://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
geocodeResults = resp || [];
drawList();
});
}
}
-171
View File
@@ -1,171 +0,0 @@
iD.ui.Geocoder = function(context) {
var key = 'f';
function resultExtent(bounds) {
return new iD.geo.Extent(
[parseFloat(bounds[3]), parseFloat(bounds[0])],
[parseFloat(bounds[2]), parseFloat(bounds[1])]);
}
function truncate(d) {
if (d.display_name.length > 80) {
return d.display_name.substr(0, 80) + '…';
} else {
return d.display_name;
}
}
function geocoder(selection) {
var shown = false;
function keydown() {
if (d3.event.keyCode !== 13) return;
d3.event.preventDefault();
var searchVal = this.value;
inputNode.classed('loading', true);
d3.json('http://nominatim.openstreetmap.org/search/' +
encodeURIComponent(searchVal) + '?limit=10&format=json', function(err, resp) {
inputNode.classed('loading', false);
if (err) return hide();
if (!resp.length) {
resultsList.html('')
.call(iD.ui.Toggle(true))
.append('span')
.attr('class', 'not-found')
.text(t('geocoder.no_results', { name: searchVal }));
} else if (resp.length > 1) {
var spans = resultsList.html('').selectAll('span')
.data(resp, function(d) { return d.place_id; });
spans.enter()
.append('span')
.text(function(d) {
return d.type.charAt(0).toUpperCase() + d.type.slice(1) + ': ';
})
.append('a')
.attr('tabindex', 1)
.text(truncate)
.on('click', clickResult)
.on('keydown', function(d) {
// support tabbing to and accepting this
// entry
if (d3.event.keyCode == 13) clickResult(d);
});
spans.exit().remove();
resultsList.call(iD.ui.Toggle(true));
} else {
hide();
applyBounds(resultExtent(resp[0].boundingbox));
selectId(resp[0].osm_type, resp[0].osm_id);
}
});
}
function clickResult(d) {
selectId(d.osm_type, d.osm_id);
applyBounds(resultExtent(d.boundingbox));
}
function applyBounds(extent) {
var map = context.map();
map.extent(extent);
if (map.zoom() > 19) map.zoom(19);
}
function selectId(type, id) {
id = type[0] + id;
if (context.hasEntity(id)) {
context.enter(iD.modes.Select(context, [id]));
} else {
context.map().on('drawn.geocoder', function() {
if (!context.hasEntity(id)) return;
context.map().on('drawn.geocoder', null);
context.enter(iD.modes.Select(context, [id]));
});
}
}
var tooltip = bootstrap.tooltip()
.placement('right')
.html(true)
.title(iD.ui.tooltipHtml(t('geocoder.title'), key));
var gcForm = selection.append('form');
var inputNode = gcForm.attr('class', 'fillL map-overlay content hide')
.append('input')
.attr({ type: 'text', placeholder: t('geocoder.placeholder') })
.attr('tabindex', 1)
.on('keydown', keydown);
var resultsList = selection.append('div')
.attr('class', 'fillL map-overlay hide');
var keybinding = d3.keybinding('geocoder');
function hide() { setVisible(false); }
function toggle() {
if (d3.event) d3.event.preventDefault();
tooltip.hide(button);
setVisible(!button.classed('active'));
}
function setVisible(show) {
if (show !== shown) {
button.classed('active', show);
shown = show;
if (!show && !resultsList.classed('hide')) {
resultsList.call(iD.ui.Toggle(show));
// remove results so that they lose focus. if the user has
// tabbed into the list, then they will have focus still,
// even if they're hidden.
resultsList.selectAll('span').remove();
}
if (show) {
selection.on('mousedown.geocoder-inside', function() {
return d3.event.stopPropagation();
});
gcForm.style('display', 'block')
.style('left', '0px')
.transition()
.duration(200)
.style('left', '-260px');
inputNode.node().focus();
} else {
selection.on('mousedown.geocoder-inside', null);
gcForm.style('display', 'block')
.style('left', '-260px')
.transition()
.duration(200)
.style('left', '0px')
.each('end', function() {
d3.select(this).style('display', 'none');
});
inputNode.node().blur();
}
}
}
var button = selection.append('button')
.attr('tabindex', -1)
.on('click', toggle)
.call(tooltip);
button.append('span')
.attr('class', 'icon geocode');
keybinding.on(key, toggle);
d3.select(document)
.call(keybinding);
context.surface().on('mousedown.geocoder-outside', hide);
context.container().on('mousedown.b.geocoder-outside', hide);
}
return geocoder;
};
-2
View File
@@ -75,7 +75,6 @@
<script src='../js/id/ui/background.js'></script>
<script src='../js/id/ui/modes.js'></script>
<script src='../js/id/ui/contributors.js'></script>
<script src='../js/id/ui/geocoder.js'></script>
<script src='../js/id/ui/geolocate.js'></script>
<script src='../js/id/ui/notice.js'></script>
<script src='../js/id/ui/flash.js'></script>
@@ -245,7 +244,6 @@
<script src="spec/ui/inspector.js"></script>
<script src="spec/ui/raw_tag_editor.js"></script>
<script src="spec/ui/geocoder.js"></script>
<script src="spec/ui/modal.js"></script>
<script src="spec/ui/flash.js"></script>
<script src="spec/ui/confirm.js"></script>
-1
View File
@@ -75,7 +75,6 @@
<script src="spec/ui/inspector.js"></script>
<script src="spec/ui/raw_tag_editor.js"></script>
<script src="spec/ui/geocoder.js"></script>
<script src="spec/ui/modal.js"></script>
<script src="spec/ui/flash.js"></script>
<script src="spec/ui/confirm.js"></script>
-6
View File
@@ -1,6 +0,0 @@
describe("iD.ui.Geocoder", function () {
it('can be instantiated', function () {
var geocoder = iD.ui.Geocoder();
expect(geocoder).to.be.ok;
});
});