From 30f245ce24bb5c87c591d7054d0053eec9dcded9 Mon Sep 17 00:00:00 2001 From: Nazar-Kutz Date: Mon, 17 Aug 2020 15:01:38 +0300 Subject: [PATCH 1/3] Fix #9585 (Show photos from Wikimedia Commons) --- .../main/java/net/osmand/data/Amenity.java | 1 + .../builders/AmenityMenuBuilder.java | 12 +- .../builders/cards/ImageCard.java | 12 +- .../plus/wikimedia/WikiImageHelper.java | 176 ++++++++++++------ 4 files changed, 142 insertions(+), 59 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java index 38854cb33b..a5c68f1938 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java @@ -39,6 +39,7 @@ public class Amenity extends MapObject { public static final String CONTENT = "content"; public static final String CUISINE = "cuisine"; public static final String WIKIDATA = "wikidata"; + public static final String WIKIMEDIA_COMMONS = "wikimedia_commons"; public static final String DISH = "dish"; public static final String REF = "ref"; public static final String OSM_DELETE_VALUE = "delete"; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java index fa1d0e7e6e..5a4618ca4f 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java @@ -471,7 +471,9 @@ public class AmenityMenuBuilder extends MenuBuilder { } textPrefix = app.getString(R.string.poi_cuisine); vl = sb.toString(); - } else if (key.contains(Amenity.ROUTE) || key.equals(Amenity.WIKIDATA)) { + } else if (key.contains(Amenity.ROUTE) + || key.equals(Amenity.WIKIDATA) + || key.equals(Amenity.WIKIMEDIA_COMMONS)) { continue; } else { if (key.contains(Amenity.DESCRIPTION)) { @@ -784,7 +786,8 @@ public class AmenityMenuBuilder extends MenuBuilder { Map additionalInfo = amenity.getAdditionalInfo(); String imageValue = additionalInfo.get("image"); String mapillaryValue = additionalInfo.get("mapillary"); - String wikidataValue = additionalInfo.get("wikidata"); + String wikidataValue = additionalInfo.get(Amenity.WIKIDATA); + String wikimediaValue = additionalInfo.get(Amenity.WIKIMEDIA_COMMONS); if (!Algorithms.isEmpty(imageValue)) { params.put("osm_image", imageValue); } @@ -792,7 +795,10 @@ public class AmenityMenuBuilder extends MenuBuilder { params.put("osm_mapillary_key", mapillaryValue); } if (!Algorithms.isEmpty(wikidataValue)) { - params.put("wikidata_id", wikidataValue); + params.put(Amenity.WIKIDATA, wikidataValue); + } + if (!Algorithms.isEmpty(wikimediaValue)) { + params.put(Amenity.WIKIMEDIA_COMMONS, wikimediaValue); } return params; } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java index 94c045fa4d..c7cf92df0f 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java @@ -17,6 +17,7 @@ import androidx.appcompat.widget.AppCompatButton; import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidUtils; import net.osmand.Location; +import net.osmand.data.Amenity; import net.osmand.data.LatLon; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -438,10 +439,15 @@ public abstract class ImageCard extends AbstractCard { pms.put("lang", preferredLang); } if (this.params != null) { - String wikidataId = this.params.get("wikidata_id"); + String wikidataId = this.params.get(Amenity.WIKIDATA); if (wikidataId != null) { - this.params.remove("wikidata_id"); - WikiImageHelper.fillWikiMediaCards(mapActivity, wikidataId, result); + this.params.remove(Amenity.WIKIDATA); + WikiImageHelper.addWikidataImageCards(mapActivity, wikidataId, result); + } + String wikimediaContent = this.params.get(Amenity.WIKIMEDIA_COMMONS); + if (wikimediaContent != null) { + this.params.remove(Amenity.WIKIMEDIA_COMMONS); + WikiImageHelper.addWikimediaImageCards(mapActivity, wikimediaContent, result); } pms.putAll(this.params); } diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java index eded246941..68f3545129 100644 --- a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java @@ -23,63 +23,113 @@ import java.util.List; public class WikiImageHelper { private static final String WIKIDATA_API_ENDPOINT = "https://www.wikidata.org/w/api.php"; - private static final String ACTION = "?action=wbgetclaims&property=P18&entity="; + private static final String WIKIMEDIA_API_ENDPOINT = "https://commons.wikimedia.org/w/api.php"; + private static final String WIKIDATA_ACTION = "?action=wbgetclaims&property=P18&entity="; + private static final String WIKIMEDIA_ACTION = "?action=query&list=categorymembers&cmtitle="; + private static final String CM_LIMIT = "&cmlimit=500"; private static final String FORMAT_JSON = "&format=json"; private static final String IMAGE_BASE_URL = "https://upload.wikimedia.org/wikipedia/commons/"; + private static final String WIKIDATA_PREFIX = "Q"; + private static final String WIKIMEDIA_FILE = "File:"; + private static final String WIKIMEDIA_CATEGORY = "Category:"; + private static final int THUMB_SIZE = 500; private static final Log LOG = PlatformUtil.getLog(WikiImageHelper.class); - public static void fillWikiMediaCards(@NonNull MapActivity mapActivity, @NonNull String wikidata, - List images) { - if (wikidata.startsWith(WIKIDATA_PREFIX)) { - StringBuilder rawResponse = new StringBuilder(); - String url = WIKIDATA_API_ENDPOINT + ACTION + wikidata + FORMAT_JSON; - String error = NetworkUtils.sendGetRequest(url, null, rawResponse); - if (error == null) { - try { - Gson gson = new Gson(); - WikipediaResponse response = gson.fromJson(rawResponse.toString(), WikipediaResponse.class); - for (WikiImage img : getImageData(response)) { - images.add(new WikiImageCard(mapActivity, img)); + public static void addWikidataImageCards(@NonNull MapActivity mapActivity, @NonNull String wikidataId, + @NonNull List imageCards) { + if (wikidataId.startsWith(WIKIDATA_PREFIX)) { + String url = WIKIDATA_API_ENDPOINT + WIKIDATA_ACTION + wikidataId + FORMAT_JSON; + WikidataResponse response = + (WikidataResponse) sendWikipediaApiRequest(url, WikidataResponse.class); + if (response != null) { + for (P18 p18 : response.claims.p18) { + String imageFileName = p18.mainsnak.datavalue.value; + if (imageFileName != null) { + addImageCard(mapActivity, imageCards, imageFileName); } - return; - } catch (JsonSyntaxException e) { - error = e.getLocalizedMessage(); } } - LOG.error(error); } else { - LOG.error("Wrong WikiMedia ID"); + LOG.error("Wrong Wikidata ID"); } } - private static List getImageData(WikipediaResponse response) { - List images = new ArrayList<>(); - for (P18 p18 : response.claims.p18) { - String imageFileName = p18.mainsnak.datavalue.value; - if (imageFileName != null) { - try { - String imageName = URLDecoder.decode(imageFileName, "UTF-8"); - imageFileName = imageName.replace(" ", "_"); - imageName = imageName.substring(0, imageName.lastIndexOf(".")); - String[] urlHashParts = getHash(imageFileName); - - String imageHiResUrl = IMAGE_BASE_URL + - urlHashParts[0] + "/" + urlHashParts[1] + "/" + - imageFileName; - String imageStubUrl = IMAGE_BASE_URL + "thumb/" + - urlHashParts[0] + "/" + urlHashParts[1] + "/" + - imageFileName + "/" + THUMB_SIZE + "px-" + - imageFileName; - images.add(new WikiImage(imageName, imageStubUrl, imageHiResUrl)); - - } catch (UnsupportedEncodingException e) { - LOG.error(e.getLocalizedMessage()); + public static void addWikimediaImageCards(@NonNull MapActivity mapActivity, @NonNull String wikiMediaTagContent, + @NonNull List imageCards) { + if (wikiMediaTagContent.startsWith(WIKIMEDIA_FILE)) { + String fileName = wikiMediaTagContent.substring(WIKIMEDIA_FILE.length()); + addImageCard(mapActivity, imageCards, fileName); + } else if (wikiMediaTagContent.startsWith(WIKIMEDIA_CATEGORY)) { + String url = WIKIMEDIA_API_ENDPOINT + WIKIMEDIA_ACTION + wikiMediaTagContent + CM_LIMIT + FORMAT_JSON; + WikimediaResponse response = + (WikimediaResponse) sendWikipediaApiRequest(url, WikimediaResponse.class); + if (response != null) { + List subCategories = new ArrayList<>(); + for (Categorymember cm : response.query.categorymembers) { + String memberTitle = cm.title; + if (memberTitle != null) { + if (memberTitle.startsWith(WIKIMEDIA_CATEGORY)) { + subCategories.add(memberTitle); + } else { + addWikimediaImageCards(mapActivity, memberTitle, imageCards); + } + } + } + for (String subCategory : subCategories) { + addWikimediaImageCards(mapActivity, subCategory, imageCards); } } + } else { + LOG.error("Wrong Wikimedia category member"); } - return images; + } + + private static Object sendWikipediaApiRequest(@NonNull String url, @NonNull Class responseClass) { + StringBuilder rawResponse = new StringBuilder(); + String errorMessage = NetworkUtils.sendGetRequest(url, null, rawResponse); + if (errorMessage == null) { + try { + return new Gson().fromJson(rawResponse.toString(), responseClass); + } catch (JsonSyntaxException e) { + errorMessage = e.getLocalizedMessage(); + } + } + LOG.error(errorMessage); + return null; + } + + private static void addImageCard(@NonNull MapActivity mapActivity, @NonNull List images, + @NonNull String fileName) { + WikiImage img = getImageData(fileName); + if (img != null) { + images.add(new WikiImageCard(mapActivity, img)); + } + } + + private static WikiImage getImageData(@NonNull String imageFileName) { + try { + String imageName = URLDecoder.decode(imageFileName, "UTF-8"); + imageFileName = imageName.replace(" ", "_"); + imageName = imageName.substring(0, imageName.lastIndexOf(".")); + String[] urlHashParts = getHash(imageFileName); + + String imageHiResUrl = IMAGE_BASE_URL + + urlHashParts[0] + "/" + urlHashParts[1] + "/" + + imageFileName; + + String imageStubUrl = IMAGE_BASE_URL + "thumb/" + + urlHashParts[0] + "/" + urlHashParts[1] + "/" + + imageFileName + "/" + THUMB_SIZE + "px-" + + imageFileName; + + return new WikiImage(imageName, imageStubUrl, imageHiResUrl); + + } catch (UnsupportedEncodingException e) { + LOG.error(e.getLocalizedMessage()); + } + return null; } @NonNull @@ -88,19 +138,23 @@ public class WikiImageHelper { return new String[]{md5.substring(0, 1), md5.substring(0, 2)}; } + // Wikidata response classes + private static class WikidataResponse { + @SerializedName("claims") + @Expose + private Claims claims; + } + private static class Claims { @SerializedName("P18") @Expose private List p18 = null; } - private static class Datavalue { - @SerializedName("value") + private static class P18 { + @SerializedName("mainsnak") @Expose - private String value; - @SerializedName("type") - @Expose - private String type; + private Mainsnak mainsnak; } private static class Mainsnak { @@ -112,15 +166,31 @@ public class WikiImageHelper { private String datatype; } - private static class P18 { - @SerializedName("mainsnak") + private static class Datavalue { + @SerializedName("value") @Expose - private Mainsnak mainsnak; + private String value; + @SerializedName("type") + @Expose + private String type; } - private static class WikipediaResponse { - @SerializedName("claims") + // Wikimedia response classes + public class WikimediaResponse { + @SerializedName("query") @Expose - private Claims claims; + private Query query; + } + + public class Query { + @SerializedName("categorymembers") + @Expose + private List categorymembers; + } + + public class Categorymember { + @SerializedName("title") + @Expose + private String title; } } From 265f488874803d7769378ed595da5787fcad2f12 Mon Sep 17 00:00:00 2001 From: Nazar-Kutz Date: Thu, 20 Aug 2020 09:08:27 +0300 Subject: [PATCH 2/3] refactoring --- .../osmand/plus/wikimedia/WikiImageHelper.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java index 68f3545129..b02af687bb 100644 --- a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java @@ -26,7 +26,7 @@ public class WikiImageHelper { private static final String WIKIMEDIA_API_ENDPOINT = "https://commons.wikimedia.org/w/api.php"; private static final String WIKIDATA_ACTION = "?action=wbgetclaims&property=P18&entity="; private static final String WIKIMEDIA_ACTION = "?action=query&list=categorymembers&cmtitle="; - private static final String CM_LIMIT = "&cmlimit=500"; + private static final String CM_LIMIT = "&cmlimit=5000"; private static final String FORMAT_JSON = "&format=json"; private static final String IMAGE_BASE_URL = "https://upload.wikimedia.org/wikipedia/commons/"; @@ -41,8 +41,7 @@ public class WikiImageHelper { @NonNull List imageCards) { if (wikidataId.startsWith(WIKIDATA_PREFIX)) { String url = WIKIDATA_API_ENDPOINT + WIKIDATA_ACTION + wikidataId + FORMAT_JSON; - WikidataResponse response = - (WikidataResponse) sendWikipediaApiRequest(url, WikidataResponse.class); + WikidataResponse response = sendWikipediaApiRequest(url, WikidataResponse.class); if (response != null) { for (P18 p18 : response.claims.p18) { String imageFileName = p18.mainsnak.datavalue.value; @@ -63,8 +62,7 @@ public class WikiImageHelper { addImageCard(mapActivity, imageCards, fileName); } else if (wikiMediaTagContent.startsWith(WIKIMEDIA_CATEGORY)) { String url = WIKIMEDIA_API_ENDPOINT + WIKIMEDIA_ACTION + wikiMediaTagContent + CM_LIMIT + FORMAT_JSON; - WikimediaResponse response = - (WikimediaResponse) sendWikipediaApiRequest(url, WikimediaResponse.class); + WikimediaResponse response = sendWikipediaApiRequest(url, WikimediaResponse.class); if (response != null) { List subCategories = new ArrayList<>(); for (Categorymember cm : response.query.categorymembers) { @@ -86,7 +84,7 @@ public class WikiImageHelper { } } - private static Object sendWikipediaApiRequest(@NonNull String url, @NonNull Class responseClass) { + private static T sendWikipediaApiRequest(@NonNull String url, @NonNull Class responseClass) { StringBuilder rawResponse = new StringBuilder(); String errorMessage = NetworkUtils.sendGetRequest(url, null, rawResponse); if (errorMessage == null) { @@ -176,19 +174,19 @@ public class WikiImageHelper { } // Wikimedia response classes - public class WikimediaResponse { + private static class WikimediaResponse { @SerializedName("query") @Expose private Query query; } - public class Query { + private static class Query { @SerializedName("categorymembers") @Expose private List categorymembers; } - public class Categorymember { + private static class Categorymember { @SerializedName("title") @Expose private String title; From 84b710772a2331805b50d7475fe614ef6893f4a5 Mon Sep 17 00:00:00 2001 From: Nazar-Kutz Date: Thu, 20 Aug 2020 09:09:43 +0300 Subject: [PATCH 3/3] change members limit --- OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java index b02af687bb..27d657d960 100644 --- a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java +++ b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java @@ -26,7 +26,7 @@ public class WikiImageHelper { private static final String WIKIMEDIA_API_ENDPOINT = "https://commons.wikimedia.org/w/api.php"; private static final String WIKIDATA_ACTION = "?action=wbgetclaims&property=P18&entity="; private static final String WIKIMEDIA_ACTION = "?action=query&list=categorymembers&cmtitle="; - private static final String CM_LIMIT = "&cmlimit=5000"; + private static final String CM_LIMIT = "&cmlimit=500"; private static final String FORMAT_JSON = "&format=json"; private static final String IMAGE_BASE_URL = "https://upload.wikimedia.org/wikipedia/commons/";