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 a041d6f694..38854cb33b 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/Amenity.java @@ -38,6 +38,7 @@ public class Amenity extends MapObject { public static final String COLLECTION_TIMES = "collection_times"; public static final String CONTENT = "content"; public static final String CUISINE = "cuisine"; + public static final String WIKIDATA = "wikidata"; 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 66567bc76c..eb1e0df1eb 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/AmenityMenuBuilder.java @@ -471,7 +471,7 @@ public class AmenityMenuBuilder extends MenuBuilder { } textPrefix = app.getString(R.string.poi_cuisine); vl = sb.toString(); - } else if (key.contains(Amenity.ROUTE)) { + } else if (key.contains(Amenity.ROUTE) || key.contains(Amenity.WIKIDATA)) { continue; } else { if (key.contains(Amenity.DESCRIPTION)) { @@ -784,12 +784,16 @@ public class AmenityMenuBuilder extends MenuBuilder { Map additionalInfo = amenity.getAdditionalInfo(); String imageValue = additionalInfo.get("image"); String mapillaryValue = additionalInfo.get("mapillary"); + String wikidataValue = additionalInfo.get("wikidata"); if (!Algorithms.isEmpty(imageValue)) { params.put("osm_image", imageValue); } if (!Algorithms.isEmpty(mapillaryValue)) { params.put("osm_mapillary_key", mapillaryValue); } + if (!Algorithms.isEmpty(wikidataValue)) { + params.put("wikidata_id", wikidataValue); + } 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 a821d8b3cf..e0733d7d56 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/builders/cards/ImageCard.java @@ -25,6 +25,7 @@ import net.osmand.plus.activities.MapActivity; import net.osmand.plus.mapcontextmenu.MenuBuilder; import net.osmand.plus.mapillary.MapillaryContributeCard; import net.osmand.plus.mapillary.MapillaryImageCard; +import net.osmand.plus.wikimedia.WikiImageHelper; import net.osmand.util.Algorithms; import org.json.JSONArray; @@ -438,6 +439,10 @@ public abstract class ImageCard extends AbstractCard { } if (this.params != null) { pms.putAll(this.params); + String wikidataId = this.params.get("wikidata_id"); + if (wikidataId != null) { + WikiImageHelper.fillWikiMediaCards(mapActivity, wikidataId, result); + } } String response = AndroidNetworkUtils.sendRequest(app, "https://osmand.net/api/cm_place", pms, "Requesting location images...", false, false); diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImage.java b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImage.java new file mode 100644 index 0000000000..d442afbb4e --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImage.java @@ -0,0 +1,33 @@ +package net.osmand.plus.wikimedia; + +public class WikiImage { + + private String imageName; + private String imageStubUrl; + private String imageHiResUrl; + private String datatype; + + public WikiImage(String imageName, String imageStubUrl, + String imageHiResUrl, String datatype) { + this.imageName = imageName; + this.imageStubUrl = imageStubUrl; + this.imageHiResUrl = imageHiResUrl; + this.datatype = datatype; + } + + public String getImageName() { + return imageName; + } + + public String getImageStubUrl() { + return imageStubUrl; + } + + public String getImageHiResUrl() { + return imageHiResUrl; + } + + public String getDatatype() { + return datatype; + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageCard.java b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageCard.java new file mode 100644 index 0000000000..db4ccef2ea --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageCard.java @@ -0,0 +1,41 @@ +package net.osmand.plus.wikimedia; + +import android.view.View; + +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard; +import net.osmand.util.Algorithms; + +import org.json.JSONObject; + +public class WikiImageCard extends ImageCard { + + public WikiImageCard(final MapActivity mapActivity, final JSONObject imageObject, + final WikiImage wikiImage) { + super(mapActivity, imageObject); + + if (topIconId == 0) { + topIconId = R.drawable.ic_logo_wikimedia; + } + + this.imageUrl = wikiImage.getImageStubUrl(); + this.url = this.imageUrl; + + View.OnClickListener onClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + openUrl(getMapActivity(), getMyApplication(), getTitle(), wikiImage.getImageHiResUrl(), + isExternalLink() || Algorithms.isEmpty(getImageHiresUrl()), + !Algorithms.isEmpty(getImageHiresUrl())); + } + }; + + if (!Algorithms.isEmpty(buttonText)) { + this.onButtonClickListener = onClickListener; + } else { + this.onClickListener = onClickListener; + } + } + +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java new file mode 100644 index 0000000000..3a2c23fdec --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/WikiImageHelper.java @@ -0,0 +1,90 @@ +package net.osmand.plus.wikimedia; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import net.osmand.PlatformUtil; +import net.osmand.osm.io.NetworkUtils; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.mapcontextmenu.builders.cards.ImageCard; +import net.osmand.plus.wikimedia.pojo.P18; +import net.osmand.plus.wikimedia.pojo.WikipediaResponse; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.logging.Log; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +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 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 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, null, img)); + } + return; + } catch (JsonSyntaxException e) { + error = e.getLocalizedMessage(); + } + LOG.error(error); + } + } else { + LOG.error("Wrong WikiMedia ID"); + } + } + + public static List getImageData(WikipediaResponse response) { + List images = new ArrayList<>(); + try { + for (P18 p18 : response.getClaims().getP18()) { + String imageFileName = p18.getMainsnak().getDatavalue().getValue(); + if (imageFileName != null) { + 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, p18.getMainsnak().getDatatype())); + } + } + } catch (UnsupportedEncodingException e) { + LOG.error(e.getLocalizedMessage()); + } + return images; + } + + @NonNull + public static String[] getHash(@NonNull String s) { + String md5 = new String(Hex.encodeHex(DigestUtils.md5(s))); + return new String[]{md5.substring(0, 1), md5.substring(0, 2)}; + } +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Claims.java b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Claims.java new file mode 100644 index 0000000000..0f9aacaff7 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Claims.java @@ -0,0 +1,22 @@ + +package net.osmand.plus.wikimedia.pojo; + +import java.util.List; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Claims { + + @SerializedName("P18") + @Expose + private List p18 = null; + + public List getP18() { + return p18; + } + + public void setP18(List p18) { + this.p18 = p18; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Datavalue.java b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Datavalue.java new file mode 100644 index 0000000000..975c0de82f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Datavalue.java @@ -0,0 +1,32 @@ + +package net.osmand.plus.wikimedia.pojo; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Datavalue { + + @SerializedName("value") + @Expose + private String value; + @SerializedName("type") + @Expose + private String type; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Mainsnak.java b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Mainsnak.java new file mode 100644 index 0000000000..a757043bdd --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/Mainsnak.java @@ -0,0 +1,65 @@ + +package net.osmand.plus.wikimedia.pojo; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Mainsnak { + + @SerializedName("snaktype") + @Expose + private String snaktype; + @SerializedName("property") + @Expose + private String property; + @SerializedName("hash") + @Expose + private String hash; + @SerializedName("datavalue") + @Expose + private Datavalue datavalue; + @SerializedName("datatype") + @Expose + private String datatype; + + public String getSnaktype() { + return snaktype; + } + + public void setSnaktype(String snaktype) { + this.snaktype = snaktype; + } + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public Datavalue getDatavalue() { + return datavalue; + } + + public void setDatavalue(Datavalue datavalue) { + this.datavalue = datavalue; + } + + public String getDatatype() { + return datatype; + } + + public void setDatatype(String datatype) { + this.datatype = datatype; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/pojo/P18.java b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/P18.java new file mode 100644 index 0000000000..5cecaf5112 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/P18.java @@ -0,0 +1,54 @@ + +package net.osmand.plus.wikimedia.pojo; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class P18 { + + @SerializedName("mainsnak") + @Expose + private Mainsnak mainsnak; + @SerializedName("type") + @Expose + private String type; + @SerializedName("id") + @Expose + private String id; + @SerializedName("rank") + @Expose + private String rank; + + public Mainsnak getMainsnak() { + return mainsnak; + } + + public void setMainsnak(Mainsnak mainsnak) { + this.mainsnak = mainsnak; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getRank() { + return rank; + } + + public void setRank(String rank) { + this.rank = rank; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/wikimedia/pojo/WikipediaResponse.java b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/WikipediaResponse.java new file mode 100644 index 0000000000..412f25602b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/wikimedia/pojo/WikipediaResponse.java @@ -0,0 +1,21 @@ + +package net.osmand.plus.wikimedia.pojo; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class WikipediaResponse { + + @SerializedName("claims") + @Expose + private Claims claims; + + public Claims getClaims() { + return claims; + } + + public void setClaims(Claims claims) { + this.claims = claims; + } + +}