mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-12 16:52:50 +00:00
Add support for icons from the noun project (close #5691)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@
|
||||
/.tx/tmp/
|
||||
npm-debug.log
|
||||
package-lock.json
|
||||
the_noun_project.auth
|
||||
transifex.auth
|
||||
|
||||
# autogenerated symlinks
|
||||
|
||||
@@ -21,6 +21,16 @@ const far = require('@fortawesome/free-regular-svg-icons').far;
|
||||
const fab = require('@fortawesome/free-brands-svg-icons').fab;
|
||||
fontawesome.library.add(fas, far, fab);
|
||||
|
||||
/*
|
||||
* The Noun Project doesn't allow anonymous API access. New "tnp-" icons will
|
||||
* not be downloaded without a the_noun_project.auth file with a json object:
|
||||
* {
|
||||
* "consumer_key": "xxxxxx",
|
||||
* "consumer_secret": "xxxxxx"
|
||||
* }
|
||||
* */
|
||||
const nounAuth = JSON.parse(fs.readFileSync('./the_noun_project.auth', 'utf8'));
|
||||
const request = require('request').defaults({ maxSockets: 1 });
|
||||
|
||||
module.exports = function buildData() {
|
||||
var building;
|
||||
@@ -57,6 +67,8 @@ module.exports = function buildData() {
|
||||
'fas-long-arrow-alt-right': {}
|
||||
};
|
||||
|
||||
var tnpIcons = {};
|
||||
|
||||
// Start clean
|
||||
shell.rm('-f', [
|
||||
'data/presets/categories.json',
|
||||
@@ -68,9 +80,9 @@ module.exports = function buildData() {
|
||||
'svg/fontawesome/*.svg',
|
||||
]);
|
||||
|
||||
var categories = generateCategories(tstrings, faIcons);
|
||||
var categories = generateCategories(tstrings, faIcons, tnpIcons);
|
||||
var fields = generateFields(tstrings, faIcons);
|
||||
var presets = generatePresets(tstrings, faIcons);
|
||||
var presets = generatePresets(tstrings, faIcons, tnpIcons);
|
||||
var defaults = read('data/presets/defaults.json');
|
||||
var translations = generateTranslations(fields, presets, tstrings);
|
||||
var taginfo = generateTaginfo(presets, fields);
|
||||
@@ -103,7 +115,8 @@ module.exports = function buildData() {
|
||||
prettyStringify(taginfo, { maxLength: 9999 })
|
||||
),
|
||||
writeEnJson(tstrings),
|
||||
writeFaIcons(faIcons)
|
||||
writeFaIcons(faIcons),
|
||||
writeTnpIcons(tnpIcons)
|
||||
];
|
||||
|
||||
return Promise.all(tasks)
|
||||
@@ -140,7 +153,7 @@ function validate(file, instance, schema) {
|
||||
}
|
||||
|
||||
|
||||
function generateCategories(tstrings, faIcons) {
|
||||
function generateCategories(tstrings, faIcons, tnpIcons) {
|
||||
var categories = {};
|
||||
glob.sync(__dirname + '/data/presets/categories/*.json').forEach(function(file) {
|
||||
var category = read(file);
|
||||
@@ -152,12 +165,16 @@ function generateCategories(tstrings, faIcons) {
|
||||
if (/^fa[srb]-/.test(category.icon)) {
|
||||
faIcons[category.icon] = {};
|
||||
}
|
||||
// noun project icon, remember for later
|
||||
if (/^tnp-/.test(category.icon)) {
|
||||
tnpIcons[category.icon] = {};
|
||||
}
|
||||
});
|
||||
return categories;
|
||||
}
|
||||
|
||||
|
||||
function generateFields(tstrings, faIcons) {
|
||||
function generateFields(tstrings, faIcons, tnpIcons) {
|
||||
var fields = {};
|
||||
glob.sync(__dirname + '/data/presets/fields/**/*.json').forEach(function(file) {
|
||||
var field = read(file);
|
||||
@@ -185,6 +202,10 @@ function generateFields(tstrings, faIcons) {
|
||||
if (/^fa[srb]-/.test(field.icon)) {
|
||||
faIcons[field.icon] = {};
|
||||
}
|
||||
// noun project icon, remember for later
|
||||
if (/^tnp-/.test(field.icon)) {
|
||||
tnpIcons[field.icon] = {};
|
||||
}
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
@@ -270,7 +291,7 @@ function stripLeadingUnderscores(str) {
|
||||
}
|
||||
|
||||
|
||||
function generatePresets(tstrings, faIcons) {
|
||||
function generatePresets(tstrings, faIcons, tnpIcons) {
|
||||
var presets = {};
|
||||
|
||||
glob.sync(__dirname + '/data/presets/presets/**/*.json').forEach(function(file) {
|
||||
@@ -290,6 +311,10 @@ function generatePresets(tstrings, faIcons) {
|
||||
if (/^fa[srb]-/.test(preset.icon)) {
|
||||
faIcons[preset.icon] = {};
|
||||
}
|
||||
// noun project icon, remember for later
|
||||
if (/^tnp-/.test(preset.icon)) {
|
||||
tnpIcons[preset.icon] = {};
|
||||
}
|
||||
});
|
||||
|
||||
presets = Object.assign(presets, suggestionsToPresets(presets));
|
||||
@@ -401,6 +426,9 @@ function generateTaginfo(presets, fields) {
|
||||
} else if (/^iD-/.test(preset.icon)) {
|
||||
tag.icon_url = 'https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/' +
|
||||
preset.icon.replace(/^iD-/, '') + '.svg?sanitize=true';
|
||||
} else if (/^tnp-/.test(preset.icon)) {
|
||||
tag.icon_url = 'https://raw.githubusercontent.com/openstreetmap/iD/master/svg/the-noun-project/' +
|
||||
preset.icon.replace(/^tnp-/, '') + '.svg?sanitize=true';
|
||||
}
|
||||
|
||||
coalesceTags(taginfo, tag);
|
||||
@@ -656,6 +684,51 @@ function writeFaIcons(faIcons) {
|
||||
}
|
||||
|
||||
|
||||
function writeTnpIcons(tnpIcons) {
|
||||
var baseURL = 'http://api.thenounproject.com/icon/';
|
||||
for (var key in tnpIcons) {
|
||||
var id = key.substring(4);
|
||||
var localPath = 'svg/the-noun-project/' + id + '.svg';
|
||||
|
||||
// don't redownload existing icons
|
||||
if (fs.existsSync(localPath)) continue;
|
||||
|
||||
if (!nounAuth) {
|
||||
console.error('No authentication file found for The Noun Project with which to download the icon with id: ' + key);
|
||||
return;
|
||||
}
|
||||
|
||||
var url = baseURL + id;
|
||||
request.get(url, { oauth : nounAuth }, handleTheNounProjectResponse);
|
||||
}
|
||||
}
|
||||
|
||||
function handleTheNounProjectResponse(err, resp, body) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
var icon = JSON.parse(body).icon;
|
||||
if (icon.license_description !== 'public-domain') {
|
||||
console.error('The icon ' + icon.term + ' (tnp-' + icon.id + ') from The Noun Project cannot be used in iD because it is not in the public domain.');
|
||||
return;
|
||||
}
|
||||
var iconURL = icon.icon_url;
|
||||
request.get(iconURL, function(err2, resp2, svg) {
|
||||
if (err2) {
|
||||
console.error(err2);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
writeFileProm('svg/the-noun-project/' + icon.id + '.svg', svg);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw (error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function writeFileProm(path, content) {
|
||||
return new Promise(function(res, rej) {
|
||||
fs.writeFile(path, content, function(err) {
|
||||
|
||||
@@ -127,7 +127,7 @@ Generally, these properties will be equivalent and should be supersets of `tags`
|
||||
|
||||
iD's validator will recommend that users add missing tags from `addTags` to matching features.
|
||||
|
||||
For example, the Bridge preset has these properties:
|
||||
For example, the Bridge preset has these properties:
|
||||
|
||||
```
|
||||
"tags": {
|
||||
@@ -143,16 +143,22 @@ When adding a feature with this preset, it will be given the tags `man_made=brid
|
||||
|
||||
##### `icon`
|
||||
|
||||
The name of a local SVG icon file. You can use icons from any of the following open source icon sets.
|
||||
The name of a local SVG icon file. You can use icons from any of the following icon sets. When specifying an icon, use the prefixed version of the name, for example `"icon": "maki-park"` or `"icon": "tnp-2009223"`.
|
||||
|
||||
* [Maki](http://www.mapbox.com/maki/) - prefix: `maki-`
|
||||
* [Temaki](http://bhousel.github.io/temaki/docs/) - prefix: `temaki-`
|
||||
* [Font Awesome (free, solid)](https://fontawesome.com/icons?d=gallery&s=solid) - prefix: `fas-`
|
||||
* [Font Awesome (free, regular)](https://fontawesome.com/icons?d=gallery&s=regular) - prefix: `far-`
|
||||
* [Font Awesome (free, brands)](https://fontawesome.com/icons?d=gallery&s=brands) - prefix: `fab-`
|
||||
* [iD's spritesheet](https://github.com/openstreetmap/iD/tree/master/svg/iD-sprite/presets) - prefix: `iD-`
|
||||
|
||||
When specifying an icon, use the prefixed version of the name, for example `"icon": "maki-park"`.
|
||||
* [iD's spritesheet](https://github.com/openstreetmap/iD/tree/master/svg/iD-sprite/presets) (`iD-`)
|
||||
* [Maki](http://www.mapbox.com/maki/) (`maki-`), map-specific icons from Mapbox
|
||||
* [Temaki](http://bhousel.github.io/temaki/docs/) (`temaki-`), an expansion pack for Maki
|
||||
* This is the best place to submit a PR if you want to create a preset icon!
|
||||
* [Font Awesome](https://fontawesome.com/icons?d=gallery&m=free), thousands of general-purpose icons
|
||||
* There is a free and pro tier. You can use any icon from the free tier in the following styles:
|
||||
* [Solid](https://fontawesome.com/icons?d=gallery&s=solid&m=free) (`fas-`)
|
||||
* [Regular](https://fontawesome.com/icons?d=gallery&s=regular&m=free) (`far-`)
|
||||
* [Brands](https://fontawesome.com/icons?d=gallery&s=brands&m=free) (`fab-`)
|
||||
* [The Noun Project](https://thenounproject.com) (`tnp-`), millions of general-purpose icons
|
||||
* The licenses vary. You can only use the public-domain icons in iD.
|
||||
* The icon styles vary. Avoid icons with thin outlines or too much detail since they will not look good at small sizes in iD.
|
||||
* Use the numeric ID of the icon (e.g. `2009223`). This is shown in the URL when you select an icon on their site.
|
||||
* Unfortunately, you must [sign up for a free API key](https://thenounproject.com/developers/) in order to download new icons (even though they're public-domain). Add a file called `the_noun_project.auth` to the root of your local iD instance containing your credentials like `{"consumer_key": "xxxxxx", "consumer_secret": "xxxxxx"}`. This file is not version-controlled.
|
||||
|
||||
##### `imageURL`
|
||||
|
||||
|
||||
@@ -540,7 +540,7 @@
|
||||
"landuse/landfill": {"geometry": ["area"], "fields": ["name"], "moreFields": ["address", "website", "phone", "email", "fax"], "tags": {"landuse": "landfill"}, "terms": ["dump"], "name": "Landfill"},
|
||||
"landuse/meadow": {"icon": "maki-garden", "geometry": ["area"], "fields": ["name"], "tags": {"landuse": "meadow"}, "terms": [], "name": "Meadow"},
|
||||
"landuse/military": {"icon": "temaki-military", "fields": ["name"], "moreFields": ["address", "website", "phone", "email", "fax"], "geometry": ["area"], "tags": {"landuse": "military"}, "terms": [], "matchScore": 0.9, "name": "Military Area"},
|
||||
"landuse/military/airfield": {"icon": "maki-airfield", "fields": ["name", "iata", "icao"], "geometry": ["point", "area"], "tags": {"military": "airfield"}, "addTags": {"aeroway": "aerodrome", "landuse": "military", "military": "airfield"}, "removeTags": {"aeroway": "aerodrome", "landuse": "military", "military": "airfield"}, "reference": {"key": "military", "value": "airfield"}, "terms": ["aerodrome", "aeroway", "air force", "airplane", "airport", "army", "base", "bomb", "fight", "force", "guard", "heli*", "jet", "marine", "navy", "plane", "troop", "war"], "name": "Military Airfield"},
|
||||
"landuse/military/airfield": {"icon": "tnp-2009265", "fields": ["name", "iata", "icao"], "geometry": ["point", "area"], "tags": {"military": "airfield"}, "addTags": {"aeroway": "aerodrome", "landuse": "military", "military": "airfield"}, "removeTags": {"aeroway": "aerodrome", "landuse": "military", "military": "airfield"}, "reference": {"key": "military", "value": "airfield"}, "terms": ["aerodrome", "aeroway", "air force", "airplane", "airport", "army", "base", "bomb", "fight", "force", "guard", "heli*", "jet", "marine", "navy", "plane", "troop", "war"], "name": "Military Airfield"},
|
||||
"landuse/military/barracks": {"icon": "temaki-military", "fields": ["name", "building_area"], "geometry": ["point", "area"], "tags": {"military": "barracks"}, "addTags": {"landuse": "military", "military": "barracks"}, "removeTags": {"landuse": "military", "military": "barracks"}, "terms": ["air force", "army", "base", "fight", "force", "guard", "marine", "navy", "troop", "war"], "name": "Barracks"},
|
||||
"landuse/military/bunker": {"icon": "temaki-military", "fields": ["name", "bunker_type", "building_area"], "geometry": ["point", "area"], "tags": {"military": "bunker"}, "addTags": {"building": "bunker", "landuse": "military", "military": "bunker"}, "removeTags": {"building": "bunker", "landuse": "military", "military": "bunker"}, "terms": ["air force", "army", "base", "fight", "force", "guard", "marine", "navy", "troop", "war"], "name": "Military Bunker"},
|
||||
"landuse/military/checkpoint": {"icon": "maki-barrier", "fields": ["name"], "geometry": ["point", "vertex", "area"], "tags": {"military": "checkpoint"}, "addTags": {"landuse": "military", "military": "checkpoint"}, "removeTags": {"landuse": "military", "military": "checkpoint"}, "terms": ["air force", "army", "base", "force", "guard", "marine", "navy", "troop", "war"], "name": "Checkpoint"},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"icon": "maki-airfield",
|
||||
"icon": "tnp-2009265",
|
||||
"fields": [
|
||||
"name",
|
||||
"iata",
|
||||
|
||||
@@ -520,7 +520,7 @@
|
||||
{"key": "landuse", "value": "landfill", "description": "🄿 Landfill", "object_types": ["area"]},
|
||||
{"key": "landuse", "value": "meadow", "description": "🄿 Meadow", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/garden-15.svg?sanitize=true"},
|
||||
{"key": "landuse", "value": "military", "description": "🄿 Military Area", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/military.svg?sanitize=true"},
|
||||
{"key": "military", "value": "airfield", "description": "🄿 Military Airfield", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/airfield-15.svg?sanitize=true"},
|
||||
{"key": "military", "value": "airfield", "description": "🄿 Military Airfield", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/the-noun-project/2009265.svg?sanitize=true"},
|
||||
{"key": "military", "value": "barracks", "description": "🄿 Barracks", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/military.svg?sanitize=true"},
|
||||
{"key": "military", "value": "bunker", "description": "🄿 Military Bunker", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/military.svg?sanitize=true"},
|
||||
{"key": "military", "value": "checkpoint", "description": "🄿 Checkpoint", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/barrier-15.svg?sanitize=true"},
|
||||
|
||||
@@ -173,7 +173,7 @@ export function svgDefs(context) {
|
||||
// add symbol spritesheets
|
||||
defs
|
||||
.call(drawDefs.addSprites, [
|
||||
'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'
|
||||
'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'tnp-sprite', 'community-sprite'
|
||||
], true);
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,8 @@ export function uiPresetIcon(context) {
|
||||
var isMaki = picon && /^maki-/.test(picon);
|
||||
var isTemaki = picon && /^temaki-/.test(picon);
|
||||
var isFa = picon && /^fa[srb]-/.test(picon);
|
||||
var isiDIcon = picon && !(isMaki || isTemaki || isFa);
|
||||
var isTnp = picon && /^tnp-/.test(picon);
|
||||
var isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
|
||||
var isCategory = !p.setTags;
|
||||
var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
|
||||
var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"dist:svg:id": "svg-sprite --symbol --symbol-dest . --shape-id-generator \"iD-%s\" --symbol-sprite dist/img/iD-sprite.svg \"svg/iD-sprite/**/*.svg\"",
|
||||
"dist:svg:community": "svg-sprite --symbol --symbol-dest . --shape-id-generator \"community-%s\" --symbol-sprite dist/img/community-sprite.svg node_modules/osm-community-index/dist/img/*.svg",
|
||||
"dist:svg:fa": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/fa-sprite.svg svg/fontawesome/*.svg",
|
||||
"dist:svg:tnp": "svg-sprite --symbol --symbol-dest . --shape-id-generator \"tnp-%s\" --symbol-sprite dist/img/tnp-sprite.svg svg/the-noun-project/*.svg",
|
||||
"dist:svg:maki": "svg-sprite --symbol --symbol-dest . --shape-id-generator \"maki-%s\" --symbol-sprite dist/img/maki-sprite.svg node_modules/@mapbox/maki/icons/*.svg",
|
||||
"dist:svg:mapillary": "svg-sprite --symbol --symbol-dest . --symbol-sprite dist/img/mapillary-sprite.svg node_modules/mapillary_sprite_source/package_signs/*.svg",
|
||||
"dist:svg:temaki": "svg-sprite --symbol --symbol-dest . --shape-id-generator \"temaki-%s\" --symbol-sprite dist/img/temaki-sprite.svg node_modules/temaki/icons/*.svg",
|
||||
|
||||
1
svg/the-noun-project/2009265.svg
Normal file
1
svg/the-noun-project/2009265.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48.00012" x="0px" y="0px"><title>Asset 578</title><g data-name="Layer 2"><g><path d="M46,26.00012a2.00006,2.00006,0,0,0-2,2v3l-12-9V17.47961A2.49323,2.49323,0,0,0,28,15.507V10.63a2.99982,2.99982,0,0,0-.21246-1.10888L25.096.74377a1.17942,1.17942,0,0,0-2.192,0L20.21246,9.52112A2.99982,2.99982,0,0,0,20,10.63v4.877a2.49323,2.49323,0,0,0-4,1.97265v4.52051l-12,9v-3a2,2,0,0,0-4,0v14a2,2,0,0,0,4,0v-2l16-4v5l-4.44916,3.811a1.86807,1.86807,0,0,0,1.32093,3.189l7.11682-1,7.13964,1a1.86807,1.86807,0,0,0,1.32093-3.189L28,41.00012v-5l16,4v2a2,2,0,1,0,4,0v-14A2.00006,2.00006,0,0,0,46,26.00012Z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 663 B |
Reference in New Issue
Block a user