mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 05:30:35 +02:00
Merge geocoder into feature search UI
This commit is contained in:
+1
-42
@@ -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
@@ -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:
|
||||
|
||||
Vendored
+2
-3
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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';
|
||||
};
|
||||
|
||||
@@ -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
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
describe("iD.ui.Geocoder", function () {
|
||||
it('can be instantiated', function () {
|
||||
var geocoder = iD.ui.Geocoder();
|
||||
expect(geocoder).to.be.ok;
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user