Ignore:
Timestamp:
2020-01-10T22:48:20+01:00 (4 years ago)
Author:
simon04
Message:

fix #18542, see #13901 - Obtain tag2link rules from Wikidata

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/util/Tag2Link.java

    r15673 r15677  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
     5import static org.openstreetmap.josm.tools.I18n.trn;
    56
     7import java.io.BufferedReader;
     8import java.io.IOException;
     9import java.net.MalformedURLException;
     10import java.net.URL;
    611import java.util.regex.Matcher;
    712import java.util.regex.Pattern;
     13import java.util.stream.Stream;
    814
     15import javax.json.Json;
     16import javax.json.JsonArray;
     17import javax.json.JsonValue;
     18
     19import com.drew.lang.Charsets;
    920import org.openstreetmap.josm.data.osm.OsmUtils;
     21import org.openstreetmap.josm.io.CachedFile;
     22import org.openstreetmap.josm.tools.Logging;
     23import org.openstreetmap.josm.tools.MultiMap;
    1024import org.openstreetmap.josm.tools.Utils;
    1125
     
    1327 * Extracts web links from OSM tags.
    1428 *
    15  * @since xxx
     29 * @since 15673
    1630 */
    17 final class Tag2Link {
     31public final class Tag2Link {
    1832
    1933    // Related implementations:
    2034    // - https://github.com/openstreetmap/openstreetmap-website/blob/master/app/helpers/browse_tags_helper.rb
     35
     36    /**
     37     * Maps OSM keys to formatter URLs from Wikidata where {@code "$1"} has to be replaced by a value.
     38     */
     39    protected static MultiMap<String, String> wikidataRules = new MultiMap<>();
    2140
    2241    private Tag2Link() {
     
    2746    interface LinkConsumer {
    2847        void acceptLink(String name, String url);
     48    }
     49
     50    /**
     51     * Initializes the tag2link rules
     52     */
     53    public static void initialize() {
     54        try {
     55            fetchRulesFromWikidata();
     56        } catch (Exception e) {
     57            Logging.error("Failed to initialize tag2link rules");
     58            Logging.error(e);
     59        }
     60    }
     61
     62    /**
     63     * Fetches rules from Wikidata using a SPARQL query.
     64     *
     65     * @throws IOException in case of I/O error
     66     */
     67    private static void fetchRulesFromWikidata() throws IOException {
     68        final String sparql = new String(new CachedFile("resource://data/tag2link.sparql").getByteContent(), Charsets.UTF_8);
     69        final CachedFile sparqlFile = new CachedFile("https://query.wikidata.org/sparql?query=" + Utils.encodeUrl(sparql))
     70                .setHttpAccept("application/json");
     71
     72        final JsonArray rules;
     73        try (BufferedReader reader = sparqlFile.getContentReader()) {
     74            rules = Json.createReader(reader).read().asJsonObject().getJsonObject("results").getJsonArray("bindings");
     75        }
     76
     77        for (JsonValue rule : rules) {
     78            final String key = rule.asJsonObject().getJsonObject("OSM_key").getString("value");
     79            final String url = rule.asJsonObject().getJsonObject("formatter_URL").getString("value");
     80            if (key.startsWith("Key:")) {
     81                wikidataRules.put(key.substring("Key:".length()), url);
     82            }
     83        }
     84        // We handle those keys ourselves
     85        Stream.of("image", "url", "website", "wikidata", "wikimedia_commons")
     86                .forEach(wikidataRules::remove);
     87
     88        Logging.info(trn(
     89                "Obtained {0} Tag2Link rule from {1}",
     90                "Obtained {0} Tag2Link rules from {1}",
     91                wikidataRules.size(), wikidataRules.size(), "Wikidata"));
    2992    }
    3093
     
    41104        final boolean valueIsURL = value.matches("^(http:|https:|www\\.).*");
    42105        if (key.matches("^(.+[:_])?website([:_].+)?$") && valueIsURL) {
    43             linkConsumer.acceptLink(tr("View website"), value);
     106            linkConsumer.acceptLink(getLinkName(value, key), value);
    44107        }
    45108        if (key.matches("^(.+[:_])?source([:_].+)?$") && valueIsURL) {
    46             linkConsumer.acceptLink(tr("View website"), value);
     109            linkConsumer.acceptLink(getLinkName(value, key), value);
    47110        }
    48111        if (key.matches("^(.+[:_])?url([:_].+)?$") && valueIsURL) {
    49             linkConsumer.acceptLink(tr("View URL"), value);
     112            linkConsumer.acceptLink(getLinkName(value, key), value);
    50113        }
    51114        if (key.matches("image") && valueIsURL) {
     
    61124        if (key.matches("(.*:)?wikidata")) {
    62125            OsmUtils.splitMultipleValues(value)
    63                     .forEach(q -> linkConsumer.acceptLink(tr("View Wikidata item"), "https://www.wikidata.org/wiki/" + q));
     126                    .forEach(q -> linkConsumer.acceptLink(tr("View Wikidata item {0}", q), "https://www.wikidata.org/wiki/" + q));
    64127        }
    65         if (key.matches("species")) {
    66             linkConsumer.acceptLink(tr("View Wikispecies page"), "https://species.wikimedia.org/wiki/" + value);
     128        if (key.matches("(.*:)?species")) {
     129            final String url = "https://species.wikimedia.org/wiki/" + value;
     130            linkConsumer.acceptLink(getLinkName(url, key), url);
    67131        }
    68132        if (key.matches("wikimedia_commons|image") && value.matches("(?i:File):.*")) {
     
    73137        }
    74138
    75         // WHC
    76         if (key.matches("ref:whc") && (valueMatcher = Pattern.compile("(?<id>[0-9]+)(-.*)?").matcher(value)).matches()) {
    77             linkConsumer.acceptLink(tr("View UNESCO sheet"), "http://whc.unesco.org/en/list/" + valueMatcher.group("id"));
    78         }
     139        wikidataRules.getValues(key).forEach(urlFormatter -> {
     140            final String url = urlFormatter.replace("$1", value);
     141            linkConsumer.acceptLink(getLinkName(url, key), url);
     142        });
     143    }
    79144
    80         // Mapillary
    81         if (key.matches("((ref|source):)?mapillary") && value.matches("[0-9a-zA-Z-_]+")) {
    82             linkConsumer.acceptLink(tr("View {0} image", "Mapillary"), "https://www.mapillary.com/map/im/" + value);
    83         }
    84 
    85         // MMSI
    86         if (key.matches("seamark:(virtual_aton|radio_station):mmsi") && value.matches("[0-9]+")) {
    87             // https://en.wikipedia.org/wiki/Maritime_Mobile_Service_Identity
    88             linkConsumer.acceptLink(tr("View MMSI on MarineTraffic"),
    89                     "https://www.marinetraffic.com/en/ais/details/ships/shipid:/mmsi:" + value);
     145    private static String getLinkName(String url, String fallback) {
     146        try {
     147            return tr("Open {0}", new URL(url).getHost());
     148        } catch (MalformedURLException e) {
     149            return tr("Open {0}", fallback);
    90150        }
    91151    }
Note: See TracChangeset for help on using the changeset viewer.