Merge branch 'master' into hcpresets

This commit is contained in:
Bryan Housel
2017-09-13 10:54:30 -04:00
42 changed files with 643 additions and 141 deletions
+6 -5
View File
@@ -1,11 +1,11 @@
# iD - friendly JavaScript editor for [OpenStreetMap](http://www.openstreetmap.org/)
# iD - friendly JavaScript editor for [OpenStreetMap](https://www.openstreetmap.org/)
[![Build Status](https://travis-ci.org/openstreetmap/iD.svg?branch=master)](https://travis-ci.org/openstreetmap/iD)
[![Greenkeeper badge](https://badges.greenkeeper.io/openstreetmap/iD.svg)](https://greenkeeper.io/)
## Basics
* iD is a JavaScript [OpenStreetMap](http://www.openstreetmap.org/) editor.
* iD is a JavaScript [OpenStreetMap](https://www.openstreetmap.org/) editor.
* It's intentionally simple. It lets you do the most basic tasks while
not breaking other people's data.
* It supports all popular modern desktop browsers: Chrome, Firefox, Safari,
@@ -27,9 +27,9 @@ if you're looking for something to do.
Come on in, the water's lovely. More help? Ping `jfire` or `bhousel` on:
* [OpenStreetMap US Slack](https://osmus-slack.herokuapp.com/)
(`#dev` or `#general` channels)
* [OpenStreetMap IRC](http://wiki.openstreetmap.org/wiki/IRC)
* [OpenStreetMap IRC](https://wiki.openstreetmap.org/wiki/IRC)
(`irc.oftc.net`, in `#iD` or `#osm-dev` or `#osm`)
* [OpenStreetMap `dev` mailing list](http://wiki.openstreetmap.org/wiki/Mailing_lists)
* [OpenStreetMap `dev` mailing list](https://wiki.openstreetmap.org/wiki/Mailing_lists)
## Prerequisites
@@ -50,7 +50,7 @@ To run the current development version of iD on your own computer:
#### Cloning the repository
The repository is reasonably large. and it's unlikely that you need the full history. If you are happy to wait for it all to download, run:
The repository is reasonably large, and it's unlikely that you need the full history. If you are happy to wait for it all to download, run:
```
git clone https://github.com/openstreetmap/iD.git
@@ -65,6 +65,7 @@ git clone --depth=1 https://github.com/openstreetmap/iD.git
If you want to add in the full history later on, perhaps to run `git blame` or `git log`, run `git fetch --depth=1000000`
#### Building iD
1. `cd` into the newly cloned project folder
2. Run `npm install`
3. Run `npm run all`
+1 -1
View File
@@ -73,7 +73,7 @@
background-color: rgba(0, 0, 0, 0.4);
padding: 0 4px;
border-radius: 4px;
transform: translate(-50%, -120%) !important;
top: -25px;
}
#mly .domRenderer .Attribution {
+4
View File
@@ -278,6 +278,10 @@ en:
title: Background
zoom: Zoom
vintage: Vintage
source: Source
description: Description
resolution: Resolution
accuracy: Accuracy
unknown: Unknown
show_tiles: Show Tiles
hide_tiles: Hide Tiles
+22
View File
@@ -630,6 +630,9 @@ en:
inscription:
# inscription=*
label: Inscription
intermittent:
# intermittent=*
label: Intermittent
internet_access:
# internet_access=*
label: Internet Access
@@ -1358,6 +1361,9 @@ en:
tourism:
# tourism=*
label: Type
tourism_attraction:
# tourism=*
label: Tourism
tower/construction:
# 'tower:construction=*'
label: Construction
@@ -2064,6 +2070,11 @@ en:
name: Excrement Bag Vending Machine
# 'terms: excrement bags,poop,dog,animal'
terms: '<translate with synonyms or related terms for ''Excrement Bag Vending Machine'', separated by commas>'
amenity/vending_machine/feminine_hygiene:
# 'amenity=vending_machine, vending=feminine_hygiene'
name: Feminine Hygiene Vending Machine
# 'terms: condom,tampon,pad,woman,women,menstrual hygiene products,personal care'
terms: '<translate with synonyms or related terms for ''Feminine Hygiene Vending Machine'', separated by commas>'
amenity/vending_machine/news_papers:
# 'amenity=vending_machine, vending=news_papers'
name: Newspaper Vending Machine
@@ -3233,6 +3244,7 @@ en:
leisure/bowling_alley:
# leisure=bowling_alley
name: Bowling Alley
# 'terms: bowling center'
terms: '<translate with synonyms or related terms for ''Bowling Alley'', separated by commas>'
leisure/common:
# leisure=common
@@ -3277,6 +3289,11 @@ en:
name: Golf Course
# 'terms: links'
terms: '<translate with synonyms or related terms for ''Golf Course'', separated by commas>'
leisure/hackerspace:
# leisure=hackerspace
name: Hackerspace
# 'terms: makerspace,hackspace,hacklab'
terms: '<translate with synonyms or related terms for ''Hackerspace'', separated by commas>'
leisure/horse_riding:
# leisure=horse_riding
name: Horseback Riding Facility
@@ -4891,6 +4908,11 @@ en:
name: Stream
# 'terms: beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse'
terms: '<translate with synonyms or related terms for ''Stream'', separated by commas>'
waterway/stream_intermittent:
# 'waterway=stream, intermittent=yes'
name: Intermittent Stream
# 'terms: arroyo,beck,branch,brook,burn,course,creek,drift,flood,flow,gully,run,runnel,rush,spate,spritz,tributary,wadi,wash,watercourse'
terms: '<translate with synonyms or related terms for ''Intermittent Stream'', separated by commas>'
waterway/water_point:
# waterway=water_point
name: Marine Drinking Water
+12
View File
@@ -875,6 +875,11 @@
"type": "textarea",
"label": "Inscription"
},
"intermittent": {
"key": "intermittent",
"type": "check",
"label": "Intermittent"
},
"internet_access": {
"key": "internet_access",
"type": "combo",
@@ -1847,6 +1852,13 @@
"type": "typeCombo",
"label": "Type"
},
"tourism_attraction": {
"key": "tourism",
"default": "attraction",
"type": "typeCombo",
"universal": true,
"label": "Tourism"
},
"tourism": {
"key": "tourism",
"type": "typeCombo",
+5
View File
@@ -0,0 +1,5 @@
{
"key": "intermittent",
"type": "check",
"label": "Intermittent"
}
@@ -0,0 +1,7 @@
{
"key": "tourism",
"default": "attraction",
"type": "typeCombo",
"universal": true,
"label": "Tourism"
}
+130 -24
View File
@@ -889,8 +889,7 @@
],
"geometry": [
"point",
"vertex",
"area"
"vertex"
],
"terms": [
"bike",
@@ -1088,8 +1087,7 @@
"capacity"
],
"geometry": [
"point",
"area"
"point"
],
"tags": {
"amenity": "charging_station"
@@ -3090,6 +3088,35 @@
},
"name": "Excrement Bag Vending Machine"
},
"amenity/vending_machine/feminine_hygiene": {
"icon": "poi-vending-machine",
"fields": [
"operator",
"payment_multi",
"currency_multi"
],
"geometry": [
"point"
],
"terms": [
"condom",
"tampon",
"pad",
"woman",
"women",
"menstrual hygiene products",
"personal care"
],
"tags": {
"amenity": "vending_machine",
"vending": "feminine_hygiene"
},
"reference": {
"key": "vending",
"value": "feminine_hygiene"
},
"name": "Feminine Hygiene Vending Machine"
},
"amenity/vending_machine/newspapers": {
"icon": "poi-vending-machine",
"fields": [
@@ -3251,8 +3278,7 @@
],
"geometry": [
"point",
"vertex",
"area"
"vertex"
],
"tags": {
"amenity": "waste_basket"
@@ -6428,8 +6454,7 @@
"access"
],
"geometry": [
"line",
"area"
"line"
],
"terms": [
"hike",
@@ -8379,7 +8404,9 @@
"point",
"area"
],
"terms": [],
"terms": [
"bowling center"
],
"tags": {
"leisure": "bowling_alley"
},
@@ -8564,6 +8591,29 @@
},
"name": "Golf Course"
},
"leisure/hackerspace": {
"icon": "commercial",
"fields": [
"name",
"address",
"building_area",
"opening_hours",
"website"
],
"geometry": [
"point",
"area"
],
"terms": [
"makerspace",
"hackspace",
"hacklab"
],
"tags": {
"leisure": "hackerspace"
},
"name": "Hackerspace"
},
"leisure/horse_riding": {
"icon": "horse-riding",
"fields": [
@@ -10112,6 +10162,10 @@
},
"natural/spring": {
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"point",
"vertex"
@@ -10189,12 +10243,13 @@
},
"natural/water/lake": {
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"area"
],
"fields": [
"name"
],
"tags": {
"natural": "water",
"water": "lake"
@@ -10212,12 +10267,13 @@
},
"natural/water/pond": {
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"area"
],
"fields": [
"name"
],
"tags": {
"natural": "water",
"water": "pond"
@@ -10237,12 +10293,13 @@
},
"natural/water/reservoir": {
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"area"
],
"fields": [
"name"
],
"tags": {
"natural": "water",
"water": "reservoir"
@@ -15345,7 +15402,8 @@
"icon": "waterway-canal",
"fields": [
"name",
"width"
"width",
"intermittent"
],
"geometry": [
"line"
@@ -15374,7 +15432,8 @@
"waterway/ditch": {
"icon": "waterway-ditch",
"fields": [
"tunnel_waterway"
"tunnel_waterway",
"intermittent"
],
"geometry": [
"line"
@@ -15410,7 +15469,8 @@
"waterway/drain": {
"icon": "waterway-stream",
"fields": [
"tunnel_waterway"
"tunnel_waterway",
"intermittent"
],
"geometry": [
"line"
@@ -15449,7 +15509,8 @@
"fields": [
"name",
"tunnel_waterway",
"width"
"width",
"intermittent"
],
"geometry": [
"line"
@@ -15515,12 +15576,56 @@
},
"name": "Marine Toilet Disposal"
},
"waterway/stream_intermittent": {
"icon": "waterway-stream",
"fields": [
"name",
"tunnel_waterway",
"width",
"intermittent"
],
"geometry": [
"line"
],
"terms": [
"arroyo",
"beck",
"branch",
"brook",
"burn",
"course",
"creek",
"drift",
"flood",
"flow",
"gully",
"run",
"runnel",
"rush",
"spate",
"spritz",
"tributary",
"wadi",
"wash",
"watercourse"
],
"tags": {
"waterway": "stream",
"intermittent": "yes"
},
"reference": {
"key": "waterway",
"value": "stream"
},
"name": "Intermittent Stream"
},
"waterway/stream": {
"icon": "waterway-stream",
"fields": [
"name",
"tunnel_waterway",
"width"
"width",
"intermittent"
],
"geometry": [
"line"
@@ -15574,7 +15679,8 @@
"fields": [
"name",
"height",
"width"
"width",
"intermittent"
],
"geometry": [
"vertex"
@@ -9,8 +9,7 @@
],
"geometry": [
"point",
"vertex",
"area"
"vertex"
],
"terms": [
"bike",
@@ -5,8 +5,7 @@
"capacity"
],
"geometry": [
"point",
"area"
"point"
],
"tags": {
"amenity": "charging_station"
@@ -0,0 +1,29 @@
{
"icon": "poi-vending-machine",
"fields": [
"operator",
"payment_multi",
"currency_multi"
],
"geometry": [
"point"
],
"terms": [
"condom",
"tampon",
"pad",
"woman",
"women",
"menstrual hygiene products",
"personal care"
],
"tags": {
"amenity": "vending_machine",
"vending": "feminine_hygiene"
},
"reference": {
"key": "vending",
"value": "feminine_hygiene"
},
"name": "Feminine Hygiene Vending Machine"
}
@@ -5,8 +5,7 @@
],
"geometry": [
"point",
"vertex",
"area"
"vertex"
],
"tags": {
"amenity": "waste_basket"
+1 -2
View File
@@ -9,8 +9,7 @@
"access"
],
"geometry": [
"line",
"area"
"line"
],
"terms": [
"hike",
@@ -13,6 +13,7 @@
"area"
],
"terms": [
"bowling center"
],
"tags": {
"leisure": "bowling_alley"
@@ -0,0 +1,23 @@
{
"icon": "commercial",
"fields": [
"name",
"address",
"building_area",
"opening_hours",
"website"
],
"geometry": [
"point",
"area"
],
"terms": [
"makerspace",
"hackspace",
"hacklab"
],
"tags": {
"leisure": "hackerspace"
},
"name": "Hackerspace"
}
+4
View File
@@ -1,5 +1,9 @@
{
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"point",
"vertex"
+4 -3
View File
@@ -1,11 +1,12 @@
{
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"area"
],
"fields": [
"name"
],
"tags": {
"natural": "water",
"water": "lake"
+4 -3
View File
@@ -1,11 +1,12 @@
{
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"area"
],
"fields": [
"name"
],
"tags": {
"natural": "water",
"water": "pond"
@@ -1,11 +1,12 @@
{
"icon": "water",
"fields": [
"name",
"intermittent"
],
"geometry": [
"area"
],
"fields": [
"name"
],
"tags": {
"natural": "water",
"water": "reservoir"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"icon": "harbor",
"fields":[
"name",
"name",
"operator"
],
"geometry": [
+2 -1
View File
@@ -2,7 +2,8 @@
"icon": "waterway-canal",
"fields": [
"name",
"width"
"width",
"intermittent"
],
"geometry": [
"line"
+2 -1
View File
@@ -1,7 +1,8 @@
{
"icon": "waterway-ditch",
"fields": [
"tunnel_waterway"
"tunnel_waterway",
"intermittent"
],
"geometry": [
"line"
+2 -1
View File
@@ -1,7 +1,8 @@
{
"icon": "waterway-stream",
"fields": [
"tunnel_waterway"
"tunnel_waterway",
"intermittent"
],
"geometry": [
"line"
+2 -1
View File
@@ -3,7 +3,8 @@
"fields": [
"name",
"tunnel_waterway",
"width"
"width",
"intermittent"
],
"geometry": [
"line"
+2 -1
View File
@@ -3,7 +3,8 @@
"fields": [
"name",
"tunnel_waterway",
"width"
"width",
"intermittent"
],
"geometry": [
"line"
@@ -0,0 +1,43 @@
{
"icon": "waterway-stream",
"fields": [
"name",
"tunnel_waterway",
"width",
"intermittent"
],
"geometry": [
"line"
],
"terms": [
"arroyo",
"beck",
"branch",
"brook",
"burn",
"course",
"creek",
"drift",
"flood",
"flow",
"gully",
"run",
"runnel",
"rush",
"spate",
"spritz",
"tributary",
"wadi",
"wash",
"watercourse"
],
"tags": {
"waterway": "stream",
"intermittent": "yes"
},
"reference": {
"key": "waterway",
"value": "stream"
},
"name": "Intermittent Stream"
}
+2 -1
View File
@@ -3,7 +3,8 @@
"fields": [
"name",
"height",
"width"
"width",
"intermittent"
],
"geometry": [
"vertex"
+12
View File
@@ -555,6 +555,10 @@
"key": "vending",
"value": "excrement_bags"
},
{
"key": "vending",
"value": "feminine_hygiene"
},
{
"key": "vending",
"value": "newspapers"
@@ -1645,6 +1649,10 @@
"key": "leisure",
"value": "golf_course"
},
{
"key": "leisure",
"value": "hackerspace"
},
{
"key": "leisure",
"value": "horse_riding"
@@ -3065,6 +3073,10 @@
"key": "waterway",
"value": "sanitary_dump_station"
},
{
"key": "intermittent",
"value": "yes"
},
{
"key": "waterway",
"value": "stream"
+23 -1
View File
@@ -354,6 +354,10 @@
"title": "Background",
"zoom": "Zoom",
"vintage": "Vintage",
"source": "Source",
"description": "Description",
"resolution": "Resolution",
"accuracy": "Accuracy",
"unknown": "Unknown",
"show_tiles": "Show Tiles",
"hide_tiles": "Hide Tiles"
@@ -1619,6 +1623,9 @@
"inscription": {
"label": "Inscription"
},
"intermittent": {
"label": "Intermittent"
},
"internet_access": {
"label": "Internet Access",
"options": {
@@ -2213,6 +2220,9 @@
"tomb": {
"label": "Type"
},
"tourism_attraction": {
"label": "Tourism"
},
"tourism": {
"label": "Type"
},
@@ -2855,6 +2865,10 @@
"name": "Excrement Bag Vending Machine",
"terms": "excrement bags,poop,dog,animal"
},
"amenity/vending_machine/feminine_hygiene": {
"name": "Feminine Hygiene Vending Machine",
"terms": "condom,tampon,pad,woman,women,menstrual hygiene products,personal care"
},
"amenity/vending_machine/newspapers": {
"name": "Newspaper Vending Machine",
"terms": "newspaper"
@@ -3917,7 +3931,7 @@
},
"leisure/bowling_alley": {
"name": "Bowling Alley",
"terms": ""
"terms": "bowling center"
},
"leisure/common": {
"name": "Common",
@@ -3955,6 +3969,10 @@
"name": "Golf Course",
"terms": "links"
},
"leisure/hackerspace": {
"name": "Hackerspace",
"terms": "makerspace,hackspace,hacklab"
},
"leisure/horse_riding": {
"name": "Horseback Riding Facility",
"terms": "equestrian,stable"
@@ -5399,6 +5417,10 @@
"name": "Marine Toilet Disposal",
"terms": "Boat,Watercraft,Sanitary,Dump Station,Pumpout,Pump out,Elsan,CDP,CTDP,Chemical Toilet"
},
"waterway/stream_intermittent": {
"name": "Intermittent Stream",
"terms": "arroyo,beck,branch,brook,burn,course,creek,drift,flood,flow,gully,run,runnel,rush,spate,spritz,tributary,wadi,wash,watercourse"
},
"waterway/stream": {
"name": "Stream",
"terms": "beck,branch,brook,burn,course,creek,current,drift,flood,flow,freshet,race,rill,rindle,rivulet,run,runnel,rush,spate,spritz,surge,tide,torrent,tributary,watercourse"
+12 -2
View File
@@ -56,13 +56,23 @@ export function presetCollection(collection) {
var leading_name = _.filter(searchable, function(a) {
return leading(a.name().toLowerCase());
}).sort(function(a, b) {
var i;
var aCompare = a.name().toLowerCase(),
bCompare = b.name().toLowerCase(),
i;
// priority if search string matches preset name exactly - #4325
if (value === aCompare) return -1;
if (value === bCompare) return 1;
// priority for higher matchScore
i = b.originalScore - a.originalScore;
if (i !== 0) return i;
i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value);
// priority if search string appears earlier in preset name
i = aCompare.indexOf(value) - bCompare.indexOf(value);
if (i !== 0) return i;
// priority for shorter preset names
return a.name().length - b.name().length;
});
+5 -5
View File
@@ -47,19 +47,19 @@ export function presetPreset(id, preset, fields) {
};
var name = preset.name || '';
var origName = preset.name || '';
preset.name = function() {
if (preset.suggestion) {
id = id.split('/');
id = id[0] + '/' + id[1];
return name + ' - ' + t('presets.presets.' + id + '.name');
return origName + ' - ' + t('presets.presets.' + id + '.name');
}
return preset.t('name', {'default': name});
return preset.t('name', { 'default': origName });
};
var origTerms = (preset.terms || []).join();
preset.terms = function() {
return preset.t('terms', {'default': ''}).toLowerCase().trim().split(/\s*,+\s*/);
return preset.t('terms', { 'default': origTerms }).toLowerCase().trim().split(/\s*,+\s*/);
};
+2
View File
@@ -243,6 +243,8 @@ export function rendererBackground(context) {
backgroundSources = dataImagery.map(function(source) {
if (source.type === 'bing') {
return rendererBackgroundSource.Bing(source, dispatch);
} else if (source.id === 'EsriWorldImagery') {
return rendererBackgroundSource.Esri(source);
} else {
return rendererBackgroundSource(source);
}
+138 -7
View File
@@ -133,13 +133,15 @@ export function rendererBackgroundSource(data) {
source.copyrightNotices = function() {};
source.getVintage = function(center, tileCoord, callback) {
source.getMetadata = function(center, tileCoord, callback) {
var vintage = {
start: localeDateString(source.startDate),
end: localeDateString(source.endDate)
};
vintage.range = vintageRange(vintage);
callback(null, vintage);
var metadata = { vintage: vintage };
callback(null, metadata);
};
@@ -158,6 +160,7 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' +
key + '&jsonp={callback}',
cache = {},
inflight = {},
providers = [];
jsonpRequest(url, function(json) {
@@ -190,21 +193,26 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
};
bing.getVintage = function(center, tileCoord, callback) {
bing.getMetadata = function(center, tileCoord, callback) {
var tileId = tileCoord.slice(0, 3).join('/'),
zoom = Math.min(tileCoord[2], 21),
centerPoint = center[1] + ',' + center[0], // lat,lng
url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
'?zl=' + zoom + '&key=' + key + '&jsonp={callback}';
if (inflight[tileId]) return;
if (!cache[tileId]) {
cache[tileId] = {};
}
if (cache[tileId] && cache[tileId].vintage) {
return callback(null, cache[tileId].vintage);
if (cache[tileId] && cache[tileId].metadata) {
return callback(null, cache[tileId].metadata);
}
inflight[tileId] = true;
jsonpRequest(url, function(result) {
delete inflight[tileId];
var err = (!result && 'Unknown Error') || result.errorDetails;
if (err) {
return callback(err);
@@ -214,8 +222,10 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
};
vintage.range = vintageRange(vintage);
cache[tileId].vintage = vintage;
return callback(null, vintage);
var metadata = { vintage: vintage };
cache[tileId].metadata = metadata;
return callback(null, metadata);
}
});
};
@@ -228,6 +238,127 @@ rendererBackgroundSource.Bing = function(data, dispatch) {
};
rendererBackgroundSource.Esri = function(data) {
// don't request blank tiles, instead overzoom real tiles - #4327
// deprecated technique, but it works (for now)
if (data.template.match(/blankTile/) === null) {
data.template = data.template + '?blankTile=false';
}
var esri = rendererBackgroundSource(data),
cache = {},
inflight = {};
esri.getMetadata = function(center, tileCoord, callback) {
var tileId = tileCoord.slice(0, 3).join('/'),
zoom = Math.min(tileCoord[2], esri.scaleExtent[1]),
centerPoint = center[0] + ',' + center[1], // long, lat (as it should be)
unknown = t('info_panels.background.unknown'),
metadataLayer,
vintage = {},
metadata = {};
if (inflight[tileId]) return;
switch (true) {
case zoom >= 19:
metadataLayer = 3;
break;
case zoom >= 17:
metadataLayer = 2;
break;
case zoom >= 13:
metadataLayer = 0;
break;
default:
metadataLayer = 99;
}
// build up query using the layer appropriate to the current zoom
var url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/' + metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json&callback={callback}';
if (!cache[tileId]) {
cache[tileId] = {};
}
if (cache[tileId] && cache[tileId].metadata) {
return callback(null, cache[tileId].metadata);
}
// accurate metadata is only available >= 13
if (metadataLayer === 99) {
vintage = {
start: null,
end: null,
range: null
};
metadata = {
vintage: null,
source: unknown,
description: unknown,
resolution: unknown,
accuracy: unknown
};
callback(null, metadata);
} else {
inflight[tileId] = true;
jsonpRequest(url, function(result) {
delete inflight[tileId];
var err;
if (!result) {
err = 'Unknown Error';
} else if (result.features && result.features.length < 1) {
err = 'No Results';
} else if (result.error && result.error.message) {
err = result.error.message;
}
if (err) {
return callback(err);
} else {
// pass through the discrete capture date from metadata
var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
vintage = {
start: captureDate,
end: captureDate,
range: captureDate
};
metadata = {
vintage: vintage,
source: clean(result.features[0].attributes.NICE_NAME),
description: clean(result.features[0].attributes.NICE_DESC),
resolution: clean(result.features[0].attributes.SRC_RES),
accuracy: clean(result.features[0].attributes.SRC_ACC)
};
// append units - meters
if (isFinite(metadata.resolution)) {
metadata.resolution += ' m';
}
if (isFinite(metadata.accuracy)) {
metadata.accuracy += ' m';
}
cache[tileId].metadata = metadata;
return callback(null, metadata);
}
});
}
function clean(val) {
return String(val).trim() || unknown;
}
};
return esri;
};
rendererBackgroundSource.None = function() {
var source = rendererBackgroundSource({ id: 'none', template: '' });
+2 -2
View File
@@ -255,8 +255,8 @@ export function rendererTileLayer(context) {
.each(function(d) {
var span = d3.select(this);
var center = context.projection.invert(tileCenter(d));
source.getVintage(center, d, function(err, result) {
span.text((result && result.range) ||
source.getMetadata(center, d, function(err, result) {
span.text((result && result.vintage && result.vintage.range) ||
t('info_panels.background.vintage') + ': ' + t('info_panels.background.unknown')
);
});
+1 -1
View File
@@ -10,7 +10,7 @@ export function svgTagClasses() {
],
statuses = [
'proposed', 'construction', 'disused', 'abandoned', 'dismantled',
'razed', 'demolished', 'obliterated'
'razed', 'demolished', 'obliterated', 'intermittent'
],
secondaries = [
'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
+26 -13
View File
@@ -40,29 +40,31 @@ export function uiCommit(context) {
var osm = context.connection();
if (!osm) return;
var comment = context.storage('comment') || '',
commentDate = +context.storage('commentDate') || 0,
hashtags = context.storage('hashtags'),
// expire stored comment and hashtags after cutoff datetime - #3947
var commentDate = +context.storage('commentDate') || 0,
currDate = Date.now(),
cutoff = 2 * 86400 * 1000; // 2 days
// expire stored comment and hashtags after cutoff datetime - #3947
if (commentDate > currDate || currDate - commentDate > cutoff) {
comment = '';
hashtags = undefined;
context.storage('comment', null);
context.storage('hashtags', null);
}
var tags;
if (!changeset) {
var detected = utilDetect();
tags = {
comment: comment,
comment: context.storage('comment') || '',
created_by: ('iD ' + context.version).substr(0, 255),
imagery_used: context.history().imageryUsed().join(';').substr(0, 255),
host: detected.host.substr(0, 255),
locale: detected.locale.substr(0, 255)
};
// call findHashtags initially - this will remove stored
// hashtags if any hashtags are found in the comment - #4304
findHashtags(tags, true);
var hashtags = context.storage('hashtags');
if (hashtags) {
tags.hashtags = hashtags;
}
@@ -276,14 +278,23 @@ export function uiCommit(context) {
}
function findHashtags(tags) {
return _.unionBy(commentTags(), hashTags(), function (s) {
function findHashtags(tags, commentOnly) {
var inComment = commentTags(),
inHashTags = hashTags();
if (inComment !== null) { // when hashtags are detected in comment...
context.storage('hashtags', null); // always remove stored hashtags - #4304
if (commentOnly) { inHashTags = null; } // optionally override hashtags field
}
return _.unionBy(inComment, inHashTags, function (s) {
return s.toLowerCase();
});
// Extract hashtags from `comment`
function commentTags() {
return tags.comment.match(/#[^\s\#]+/g);
return tags.comment
.replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
.match(/#[\w-]+/g);
}
// Extract and clean hashtags from `hashtags`
@@ -293,7 +304,7 @@ export function uiCommit(context) {
.split(/[,;\s]+/)
.map(function (s) {
if (s[0] !== '#') { s = '#' + s; } // prepend '#'
var matched = s.match(/#[^\s\#]+/g); // match valid hashtags
var matched = s.match(/#[\w-]+/g); // match valid hashtags
return matched && matched[0];
}).filter(Boolean); // exclude falsey
}
@@ -327,7 +338,9 @@ export function uiCommit(context) {
});
if (!onInput) {
var arr = findHashtags(tags);
// when changing the comment, override hashtags with any found in comment.
var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
var arr = findHashtags(tags, commentOnly);
if (arr.length) {
tags.hashtags = arr.join(';').substr(0, 255);
context.storage('hashtags', tags.hashtags);
+12 -3
View File
@@ -18,6 +18,7 @@ export function uiField(context, presetField, entity, options) {
var dispatch = d3.dispatch('change'),
field = _.clone(presetField),
show = options.show,
state = '',
tags = {};
@@ -33,8 +34,6 @@ export function uiField(context, presetField, entity, options) {
field.keys = field.keys || [field.key];
field.show = options.show;
function isModified() {
if (!entity) return false;
@@ -176,8 +175,18 @@ export function uiField(context, presetField, entity, options) {
};
field.show = function() {
show = true;
if (field.default && field.key && tags[field.key] !== field.default) {
var t = {};
t[field.key] = field.default;
dispatch.call('change', this, t);
}
};
field.isShown = function() {
return field.show || _.some(field.keys, function(key) { return !!tags[key]; });
return show || _.some(field.keys, function(key) { return !!tags[key]; });
};
+1 -1
View File
@@ -101,7 +101,7 @@ export function uiFormFields(context) {
.minItems(1)
.on('accept', function (d) {
var field = d.field;
field.show = true;
field.show();
render(selection);
if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
field.focus();
+56 -36
View File
@@ -5,16 +5,18 @@ import { t } from '../../util/locale';
export function uiPanelBackground(context) {
var background = context.background();
var currSource = null;
var currZoom = '';
var currVintage = '';
var currSourceName = null;
var metadata = {};
var metadataKeys = [
'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
];
var debouncedRedraw = _.debounce(redraw, 250);
function redraw(selection) {
if (currSource !== background.baseLayerSource().name()) {
currSource = background.baseLayerSource().name();
currZoom = '';
currVintage = '';
if (currSourceName !== background.baseLayerSource().name()) {
currSourceName = background.baseLayerSource().name();
metadata = {};
}
selection.html('');
@@ -25,25 +27,20 @@ export function uiPanelBackground(context) {
list
.append('li')
.text(currSource);
.text(currSourceName);
list
.append('li')
.text(t('info_panels.background.zoom') + ': ')
.append('span')
.attr('class', 'zoom')
.text(currZoom);
metadataKeys.forEach(function(k) {
list
.append('li')
.attr('class', 'background-info-list-' + k)
.classed('hide', !metadata[k])
.text(t('info_panels.background.' + k) + ': ')
.append('span')
.attr('class', 'background-info-span-' + k)
.text(metadata[k]);
});
list
.append('li')
.text(t('info_panels.background.vintage') + ': ')
.append('span')
.attr('class', 'vintage')
.text(currVintage);
if (!currVintage) {
debouncedGetVintage(selection);
}
debouncedGetMetadata(selection);
var toggle = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
@@ -60,24 +57,47 @@ export function uiPanelBackground(context) {
}
var debouncedGetVintage = _.debounce(getVintage, 250);
function getVintage(selection) {
var debouncedGetMetadata = _.debounce(getMetadata, 250);
function getMetadata(selection) {
var tile = d3.select('.layer-background img.tile-center'); // tile near viewport center
if (tile.empty()) return;
var d = tile.datum(),
var sourceName = currSourceName,
d = tile.datum(),
zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom()),
center = context.map().center();
currZoom = String(zoom);
selection.selectAll('.zoom')
.text(currZoom);
// update zoom
metadata.zoom = String(zoom);
selection.selectAll('.background-info-list-zoom')
.classed('hide', false)
.selectAll('.background-info-span-zoom')
.text(metadata.zoom);
if (!d || !d.length >= 3) return;
background.baseLayerSource().getVintage(center, d, function(err, result) {
currVintage = (result && result.range) || t('info_panels.background.unknown');
selection.selectAll('.vintage')
.text(currVintage);
background.baseLayerSource().getMetadata(center, d, function(err, result) {
if (err || currSourceName !== sourceName) return;
// update vintage
var vintage = result.vintage;
metadata.vintage = (vintage && vintage.range) || t('info_panels.background.unknown');
selection.selectAll('.background-info-list-vintage')
.classed('hide', false)
.selectAll('.background-info-span-vintage')
.text(metadata.vintage);
// update other metdata
_.without(metadataKeys, 'zoom', 'vintage')
.forEach(function(k) {
var val = result[k];
metadata[k] = val;
selection.selectAll('.background-info-list-' + k)
.classed('hide', !val)
.selectAll('.background-info-span-' + k)
.text(val);
});
});
}
@@ -87,10 +107,10 @@ export function uiPanelBackground(context) {
context.map()
.on('drawn.info-background', function() {
selection.call(redraw);
selection.call(debouncedRedraw);
})
.on('move.info-background', function() {
selection.call(debouncedGetVintage);
selection.call(debouncedGetMetadata);
});
};
+1
View File
@@ -29,6 +29,7 @@ export function uiStatus(context) {
osm.authenticate();
});
} else {
// eslint-disable-next-line no-warning-comments
// TODO: nice messages for different error types
selection.text(t('status.error'));
}
+34 -15
View File
@@ -49,7 +49,14 @@ describe('iD.presetCollection', function() {
name: 'Park',
tags: { leisure: 'park' },
geometry: ['point', 'area'],
terms: [ 'grass' ]
terms: [ 'grass' ],
matchScore: 0.5
}),
parking: iD.presetPreset('__test/amenity/parking', {
name: 'Parking',
tags: { amenity: 'parking' },
geometry: ['point', 'area'],
terms: [ 'cars' ]
}),
soccer: iD.presetPreset('__test/leisure/pitch/soccer', {
name: 'Soccer Field',
@@ -68,7 +75,7 @@ describe('iD.presetCollection', function() {
var c = iD.presetCollection([
p.point, p.line, p.area, p.grill, p.sandpit, p.residential,
p.grass1, p.grass2, p.park, p.soccer, p.football
p.grass1, p.grass2, p.park, p.parking, p.soccer, p.football
]);
describe('#item', function() {
@@ -93,30 +100,42 @@ describe('iD.presetCollection', function() {
it('returns alternate matches in correct order', function() {
var col = c.search('gri', 'point').matchGeometry('point').collection;
expect(col.indexOf(p.grill)).to.eql(0); // 1. 'Grill' (leading name)
expect(col.indexOf(p.football)).to.eql(7); // 2. 'Football' (leading term 'gridiron')
expect(col.indexOf(p.sandpit)).to.eql(1); // 3. 'Sandpit' (leading tag value 'grit_bin')
expect(col.indexOf(p.grass1)).to.be.within(2,3); // 4. 'Grass' (similar name)
expect(col.indexOf(p.grass2)).to.be.within(3,4); // 5. 'Ğṝȁß' (similar name)
expect(col.indexOf(p.park)).to.eql(4); // 6. 'Park' (similar term 'grass')
expect(col.indexOf(p.grill), 'Grill').to.eql(0); // 1. 'Grill' (leading name)
expect(col.indexOf(p.football), 'Football').to.eql(1); // 2. 'Football' (leading term 'gridiron')
expect(col.indexOf(p.sandpit), 'Sandpit').to.eql(2); // 3. 'Sandpit' (leading tag value 'grit_bin')
expect(col.indexOf(p.grass1), 'Grass').to.be.within(3,4); // 4. 'Grass' (similar name)
expect(col.indexOf(p.grass2), 'Ğṝȁß').to.be.within(3,4); // 5. 'Ğṝȁß' (similar name)
expect(col.indexOf(p.park), 'Park').to.eql(5); // 6. 'Park' (similar term 'grass')
});
it('sorts preset with matchScore penalty below others', function() {
var col = c.search('par', 'point').matchGeometry('point').collection;
expect(col.indexOf(p.parking), 'Parking').to.eql(0); // 1. 'Parking' (default matchScore)
expect(col.indexOf(p.park), 'Park').to.eql(1); // 2. 'Park' (low matchScore)
});
it('ignores matchScore penalty for exact name match', function() {
var col = c.search('park', 'point').matchGeometry('point').collection;
expect(col.indexOf(p.park), 'Park').to.eql(0); // 1. 'Park' (low matchScore)
expect(col.indexOf(p.parking), 'Parking').to.eql(1); // 2. 'Parking' (default matchScore)
});
it('considers diacritics on exact matches', function() {
var col = c.search('ğṝȁ', 'point').matchGeometry('point').collection;
expect(col.indexOf(p.grass2)).to.eql(0); // 1. 'Ğṝȁß' (leading name)
expect(col.indexOf(p.grass1)).to.eql(1); // 2. 'Grass' (similar name)
expect(col.indexOf(p.grass2), 'Ğṝȁß').to.eql(0); // 1. 'Ğṝȁß' (leading name)
expect(col.indexOf(p.grass1), 'Grass').to.eql(1); // 2. 'Grass' (similar name)
});
it('replaces diacritics on fuzzy matches', function() {
var col = c.search('graß', 'point').matchGeometry('point').collection;
expect(col.indexOf(p.grass1)).to.be.within(0,1); // 1. 'Grass' (similar name)
expect(col.indexOf(p.grass2)).to.be.within(0,1); // 2. 'Ğṝȁß' (similar name)
expect(col.indexOf(p.grass1), 'Grass').to.be.within(0,1); // 1. 'Grass' (similar name)
expect(col.indexOf(p.grass2), 'Ğṝȁß').to.be.within(0,1); // 2. 'Ğṝȁß' (similar name)
});
it('includes the appropriate fallback preset', function() {
expect(c.search('foo', 'point').collection).to.include(p.point);
expect(c.search('foo', 'line').collection).to.include(p.line);
expect(c.search('foo', 'area').collection).to.include(p.area);
expect(c.search('foo', 'point').collection, 'point').to.include(p.point);
expect(c.search('foo', 'line').collection, 'line').to.include(p.line);
expect(c.search('foo', 'area').collection, 'area').to.include(p.area);
});
it('excludes presets with searchable: false', function() {
+2 -1
View File
@@ -20,4 +20,5 @@ mocha.setup({
});
expect = chai.expect;
var d3 = iD.d3;
// eslint-disable-next-line no-unused-vars
var d3 = iD.d3;