function gatherNames()

in modules/services/nsi.js [257:353]


function gatherNames(tags) {
  const empty = { primary: new Set(), alternate: new Set() };
  let primary = new Set();
  let alternate = new Set();
  let foundSemi = false;
  let testNameFragments = false;
  let patterns;

  // Patterns for matching OSM keys that might contain namelike values.
  // These roughly correspond to the "trees" concept in name-suggestion-index,
  let t = identifyTree(tags);
  if (!t) return empty;

  if (t === 'transit') {
    patterns = {
      primary: /^network$/i,
      alternate: /^(operator|operator:\w+|network:\w+|\w+_name|\w+_name:\w+)$/i
    };
  } else if (t === 'flags') {
    patterns = {
      primary: /^(flag:name|flag:name:\w+)$/i,
      alternate: /^(flag|flag:\w+|subject|subject:\w+)$/i   // note: no `country`, we special-case it below
    };
  } else if (t === 'brands') {
    testNameFragments = true;
    patterns = {
      primary: /^(name|name:\w+)$/i,
      alternate: /^(brand|brand:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i,
    };
  } else if (t === 'operators') {
    testNameFragments = true;
    patterns = {
      primary: /^(name|name:\w+|operator|operator:\w+)$/i,
      alternate: /^(brand|brand:\w+|\w+_name|\w+_name:\w+)/i,
    };
  } else {  // unknown/multiple
    testNameFragments = true;
    patterns = {
      primary: /^(name|name:\w+)$/i,
      alternate: /^(brand|brand:\w+|network|network:\w+|operator|operator:\w+|\w+_name|\w+_name:\w+)/i,
    };
  }

  // Test `name` fragments, longest to shortest, to fit them into a "Name Branch" pattern.
  // e.g. "TUI ReiseCenter - Neuss Innenstadt" -> ["TUI", "ReiseCenter", "Neuss", "Innenstadt"]
  if (tags.name && testNameFragments) {
    const nameParts = tags.name.split(/[\s\-\/,.]/);
    for (let split = nameParts.length; split > 0; split--) {
      const name = nameParts.slice(0, split).join(' ');  // e.g. "TUI ReiseCenter"
      primary.add(name);
    }
  }

  // Check all tags
  Object.keys(tags).forEach(osmkey => {
    const osmvalue = tags[osmkey];
    if (!osmvalue) return;

    if (isNamelike(osmkey, 'primary')) {
      if (/;/.test(osmvalue)) {
        foundSemi = true;
      } else {
        primary.add(osmvalue);
        alternate.delete(osmvalue);
      }
    } else if (!primary.has(osmvalue) && isNamelike(osmkey, 'alternate')) {
      if (/;/.test(osmvalue)) {
        foundSemi = true;
      } else {
        alternate.add(osmvalue);
      }
    }
  });

  // For flags only, fallback to `country` tag only if no other namelike values were found.
  // See https://github.com/openstreetmap/iD/pull/8305#issuecomment-769174070
  if (tags.man_made === 'flagpole' && !primary.size && !alternate.size && !!tags.country) {
    const osmvalue = tags.country;
    if (/;/.test(osmvalue)) {
      foundSemi = true;
    } else {
      alternate.add(osmvalue);
    }
  }

  // If any namelike value contained a semicolon, return empty set and don't try matching anything.
  if (foundSemi) {
    return empty;
  } else {
    return { primary: primary, alternate: alternate };
  }

  function isNamelike(osmkey, which) {
    if (osmkey === 'old_name') return false;
    return patterns[which].test(osmkey) && !notNames.test(osmkey);
  }
}