API:Visor de lugares cercanos
Esta página es parte de la documentación de la API de acciones de MediaWiki. |
Introducción
In this tutorial, you will learn how to search for wiki pages near your location using the MediaWiki Action API.
This tutorial will teach you how to do this using:
- Python and Flask framework
- JavaScript, jQuery and Bootstrap framework. The latter are loaded from Wikimedia Toolforge for privacy reasons.
- MediaWiki Action API's Geosearch module
- Geolocation API
A step-by-step process for building this application
Step 1: Set up Python and Flask development environment
To set up the Python development environment for a Flask application, you will need to install Python, create a virtual environment, and install Flask.
Here is how to set up the development environment for building the application:
$ mkdir nearby-places-viewer
$ cd nearby-places-viewer/
This will create a new directory and change into it
$ python3 --version #Python 3.6.5
This command checks your Python version
$ python3 -m venv venv
This command will create a virtual environment named 'venv'
$ source venv/bin/activate
This will activate the virtual environment
$ pip install Flask
This command will install the Flask package with all its dependencies
|
Step 2: Create a simple Flask application
Render a simple static page
Place the following code in $HOME/nearby-places-viewer/nearby.py
#!/usr/bin/python3
"""
nearby.py
MediaWiki Action API Code Samples
Nearby places viewer app: Demo of geo search for wiki pages
near a location using the Geolocation API and MediaWiki Action
API's Geosearch module.
MIT license
"""
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
""" Displays the index page accessible at '/'
"""
return render_template('places.html')
if __name__ == '__main__':
app.run()
|
Drop this one line of code <h1>Nearby places viewer</h1>
in a HTML file inside the templates
folder: $HOME/nearby-places-viewer/templates/places.html
Note:
In this simple application, we are using render_template
method which renders the template named places.html
from the templates
directory.
Next, run your Flask app with the command python nearby.py
and open http://127.0.0.1:5000/
to view your app in the browser.
You should be able to see "Nearby places viewer" in your browser window.
Style your app
Let's do some app styling.
Add a button element for accepting search input in the HTML file and link tags to load an external and internal stylesheet.
External stylesheet, in this case, is the URL of a CSS file for the font Amatic
.
Replace the existing code in $HOME/nearby-places-viewer/templates/places.html
with the following:
<link rel="stylesheet" href="//tools-static.wmflabs.org/fontcdn/css?family=Amatic+SC:700">
<link rel="stylesheet" href="/static/style.css">
<h1>Nearby places viewer</h1>
<button>Click here to search</button>
Place the following code in $HOME/nearby-places-viewer/static/style.css
Application layout
$HOME/nearby-places-viewer ├── templates/ │ └── places.html ├── static/ │ └── static.css ├── nearby.py └── venv/
Step 3: Obtain coordinates of your current location
To search for wiki pages nearby, you first need to obtain coordinates of your current location. To do so, you can use the Geolocation API along with some JavaScript code.
When you click the Click here to search button, the app makes a call to the Geolocation API and retrieves the current position of your device via the API's Navigator.geolocation
object.
The API's response in return is a Position
object from which you can obtain the latitude and longitude.
Note: When your app makes a call to the API you will be notified and asked to grant permission to your browser to access your location.
Place the following code in $HOME/nearby-places-viewer/static/places.js
$( document ).ready(function() {
var x = document.getElementById( "places-list" );
$( 'button' ).click(function() {
getLocation();
});
function getLocation() {
x.innerHTML = "Searching your location..";
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(fetchPlaces);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function fetchPlaces(position) {
x.innerHTML = position.coords.latitude + "|" + position.coords.longitude;
}
});
Link the custom JavaScript /static/places.js
and jQuery
from the HTML
file:
$HOME/nearby-places-viewer/templates/places.html
<!-- Add these two lines at the top -->
<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="/static/places.js"></script>
<!-- Add this line after the button element -->
<div id="places-list"></div>
Step 4: Send location data to the server with AJAX
This app uses jQuery's AJAX method to send the location data obtained in Step 3 to the server and POST it without page refresh to a Python Flask route /
in $HOME/nearby-places-viewer/nearby.py
.
As a next step, let's add an AJAX call to the fetchPlaces
function in $HOME/nearby-places-viewer/static/places.js
.
Note:
At this point, if you try to run your app, it is likely that you will see an error in the browser window as we have not added support to the route /
to handle POST requests.
function fetchPlaces(position) {
var data = {
"latitude": position.coords.latitude,
"longitude": position.coords.longitude
};
$.ajax({
url: "/",
type: "POST",
data: JSON.stringify(data),
contentType: "application/json",
dataType: "json",
success: function (response) {
x.innerHTML = "Success!";
},
error: function () {
x.innerHTML = "An error occurred while fetching places!";
}
});
}
Step 5: Fetch nearby places via MediaWiki Action API
First, extend the Python Flask route /
in $HOME/nearby-places-viewer/nearby.py
to handle POST requests.
You can do so by adding both GET and POST in the methods
argument list in the route decorator.
Next, you can obtain location data available in JSON
format from the request
object and pass it to fetch_places_nearby()
function for further processing.
@app.route('/', methods=['GET', 'POST'])
def index():
""" Displays the index page accessible at '/'
"""
if request.method == "POST":
data = request.get_json()
latitude = data['latitude']
longitude = data['longitude']
results = fetch_places_nearby(latitude, longitude)
return jsonify(results=results)
return render_template('places.html')
The code in the fetch_places_nearby()
function makes a GET
request to the Action API to search for wiki pages near a location.
API call consists of an endpoint https://en.wikipedia.org/w/api.php
and query string parameters.
Some of the key parameters are:
action=query
main module to query informationgenerator=geosearch
query module's submodulelist
used as a generator module to get search results for a set of pagesprop=coordinates|pageimages|description|info
tells which properties to return for pages
Note: For more information on the geosearch module visit API:Geosearch .
def fetch_places_nearby(lat, lon):
params = {
"action": "query",
"prop": "coordinates|pageimages|description|info",
"inprop": "url",
"pithumbsize": 144,
"generator": "geosearch",
"ggsradius": 10000,
"ggslimit": 10,
"ggscoord": str(lat) + "|" + str(lon),
"format": "json",
}
res = SESSION.get(url=API_ENDPOINT, params=params)
data = res.json()
places = data['query'] and data['query']['pages']
# TODO: further process 'places' list
return places
View complete Python and Flask code with import statements and processed places
list.
This app uses Haversine
package available in Python to calculate distance between two geographic coordinates.
$HOME/nearby-places-viewer/nearby.py |
---|
#!/usr/bin/python3
"""
nearby.py
MediaWiki Action API Code Samples
Nearby places viewer app: Demo of geo search for wiki pages near a location using
the Geolocation API and MediaWiki Action API's Geosearch module.
MIT license
"""
from flask import Flask, request, render_template, jsonify
import requests
from haversine import haversine
APP = Flask(__name__)
SESSION = requests.Session()
API_ENDPOINT = 'https://en.wikipedia.org/w/api.php'
@APP.route('/', methods=['GET', 'POST'])
def index():
""" Displays the index page accessible at '/'
"""
if request.method == "POST":
data = request.get_json()
latitude = data['latitude']
longitude = data['longitude']
results = fetch_places_nearby(latitude, longitude)
return jsonify(results=results)
return render_template('places.html')
def fetch_places_nearby(lat, lon):
""" Fetches nearby places via MediaWiki Action API's Geosearch module
"""
params = {
"action": "query",
"prop": "coordinates|pageimages|description|info",
"inprop": "url",
"pithumbsize": 144,
"generator": "geosearch",
"ggsradius": 10000,
"ggslimit": 10,
"ggscoord": str(lat) + "|" + str(lon),
"format": "json",
}
res = SESSION.get(url=API_ENDPOINT, params=params)
data = res.json()
places = data['query'] and data['query']['pages']
results = []
for k in places:
title = places[k]['title']
description = places[k]['description'] if "description" in places[k] else ''
thumbnail = places[k]['thumbnail']['source'] if "thumbnail" in places[k] else ''
article_url = places[k]['fullurl']
cur_loc = (lat, lon)
place_loc = (places[k]['coordinates'][0]['lat'], places[k]['coordinates'][0]['lon'])
distance = round(haversine(cur_loc, place_loc, unit='mi'), 2)
results.append({
'title': title,
'description': description,
'thumbnail': thumbnail,
'articleUrl': article_url,
'distance': distance
})
return results
if __name__ == '__main__':
APP.run(debug=True)
|
Step 6: Create a user interface for list of places from JSON response
Handle the returned JSON data from the server in AJAX's success
callback and use HTML DOM innerHTML
property to alter the div
contents.
success: function (response) {
var places = response["results"],
no_thumb = "..";
x.innerHTML = "";
for (var p in places) {
var thumbnail = places[p].thumbnail || no_thumb;
x.innerHTML += "<div class=\"item\"><div class=\"col-xs-8 no-padding\"><h5><a href=\"" +
places[p]["articleUrl"] + "\" target=\"_blank\">" +
places[p]["title"] + "</a></h5><p>" +
places[p]["description"] + "</p><span>📍" + places[p]["distance"] +
" miles</p></div><div class=\"col-xs-4 no-padding\"><img src=\"" +
thumbnail + " \"></div></div>";
}
}
View complete JavaScript code with AJAX success callback.
$HOME/nearby-places-viewer/static/places.js |
---|
$( document ).ready(function() {
var x = document.getElementById( "places-list" );
$( ".btn-search" ).click(function() {
getLocation();
});
function getLocation() {
x.innerHTML = "Searching your location..";
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(fetchPlaces);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function fetchPlaces(position) {
var data = {
"latitude": position.coords.latitude,
"longitude": position.coords.longitude
};
$.ajax({
url: "/",
type: "POST",
data: JSON.stringify(data),
contentType: "application/json",
dataType: "json",
success: function (response) {
var places = response["results"],
no_thumb = "https://upload.wikimedia.org/wikipedia/commons/thumb/7/75/Gnome-image-missing.svg/200px-Gnome-image-missing.svg.png";
x.innerHTML = "";
for (var p in places) {
var thumbnail = places[p].thumbnail || no_thumb;
x.innerHTML += "<div class=\"item\"><div class=\"col-xs-8 no-padding\"><h5><a href=\"" +
places[p]["articleUrl"] + "\" target=\"_blank\">" +
places[p]["title"] + "</a></h5><p>" +
places[p]["description"] + "</p><span>📍" + places[p]["distance"] +
" miles</p></div><div class=\"col-xs-4 no-padding\"><img src=\"" +
thumbnail + " \"></div></div>";
}
},
error: function () { x.innerHTML = "An error occurred while fetching places!"; }
});
}
});
|
Step 7: Style changes with Bootstrap
You might have noticed that the code snippet in Step 6 uses Bootstrap class names. Yes, we are integrating the Bootstrap framework in this step to add a responsive layout design to the Places UI. To do so, let's make some more tweaks to CSS and HTML files.
View complete CSS and HTML code.
$HOME/nearby-places-viewer/static/style.css |
---|
.places-container .no-padding {
padding: 0;
}
.places-container .info {
text-align: center;
}
.places-container .viewer-heading {
font-family: 'Amatic SC', cursive;
font-size: 2.5em;
font-weight: normal;
color: black;
}
.places-container .btn-search {
font-size: 16px;
padding: 10px 25px;
cursor: pointer;
text-decoration: none;
color: white;
border-radius: 4px;
background-color: #7c7ce0;
}
.places-container .list {
margin-top: 20px;
}
.places-container .item {
min-height: 100px;
}
.places-container .item p,
span {
font-size: 12px;
margin: 2px;
}
.places-container .item span {
color: gray;
}
.places-container .item img {
float: right;
width: 80px;
height: 80px;
border-radius: 5px;
object-fit: cover;
}
.places-container .item a {
color: #7c7ce0;
}
|
$HOME/nearby-places-viewer/templates/places.html |
---|
<title>Nearby places viewer</title>
<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//tools-static.wmflabs.org/cdnjs/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="/static/places.js"></script>
<link rel="stylesheet" href="//tools-static.wmflabs.org/cdnjs/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="//tools-static.wmflabs.org/fontcdn/css?family=Amatic+SC:700">
<link rel="stylesheet" href="/static/style.css">
<div class="container places-container col-lg-4 col-xs-12">
<div class="col-xs-12 no-padding info">
<h1 class="viewer-heading">Nearby places viewer</h1>
<button class="btn-search">Click here to search</button>
</div>
<div class="col-xs-12 no-padding list" id="places-list">
</div>
</div>
|
Próximos pasos
- Contribute a demo app that you have developed using the MediaWiki API to this code samples repository.
Véase también
- API:Geosearch
- Flask tutorials
- Nearby is an app for the Pebble smart watch that fetches Wikipedia articles near you.