Jump to content
MediaWiki

API:Nearby places viewer

From mediawiki.org
This page is part of the MediaWiki Action API documentation.
MediaWiki Action API
Basics
Authentication
Accounts and Users
Page Operations
Search
Developer Utilities
Tutorials
v · d · e

Overview

[edit ]

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:

A step-by-step process for building this application

[edit ]

Step 1: Set up Python and Flask development environment

[edit ]

To set up the Python development environment for a Flask application, you will need to install Python, create a virtual environment, and install Flask.

This application uses Python3, the recommended version for new Python projects. Learn more about the differences between Python2 and Python3 here. To install Python3 on your local machine, follow step-by-step instructions in these installation guides.

Here is how to set up the development environment for building the application:

$ mkdirnearby-places-viewer
$ cdnearby-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-mvenvvenv
This command will create a virtual environment named 'venv'
$ sourcevenv/bin/activate
This will activate the virtual environment
$ pipinstallFlask
This command will install the Flask package with all its dependencies

Step 2: Create a simple Flask application

[edit ]

Render a simple static page

[edit ]

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
"""
fromflaskimport Flask, render_template
app = Flask(__name__)
@app.route('/')
defindex():
""" 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

[edit ]

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

h1{
font-family:'Amatic SC',cursive;
font-size:2.5em;
font-weight:normal;
color:black;
}
button{
font-size:16px;
padding:10px25px;
cursor:pointer;
text-decoration:none;
color:white;
border-radius:4px;
background-color:#7c7ce0;
margin-bottom:20px;
}
Nearby places viewer demo app screenshot (1)

Application layout

[edit ]
$HOME/nearby-places-viewer
├── templates/
│ └── places.html
├── static/
│ └── static.css
├── nearby.py
└── venv/

Step 3: Obtain coordinates of your current location

[edit ]

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(){
varx=document.getElementById("places-list");

$('button').click(function(){
getLocation();
});
functiongetLocation(){
x.innerHTML="Searching your location..";
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(fetchPlaces);
}else{
x.innerHTML="Geolocation is not supported by this browser.";
}
}
functionfetchPlaces(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

[edit ]

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.

functionfetchPlaces(position){
vardata={
"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

[edit ]

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'])
defindex():
""" 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 information
  • generator=geosearch query module's submodule list used as a generator module to get search results for a set of pages
  • prop=coordinates|pageimages|description|info tells which properties to return for pages

Note: For more information on the geosearch module visit API:Geosearch .

deffetch_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
"""
fromflaskimport Flask, request, render_template, jsonify
importrequests
fromhaversineimport haversine
APP = Flask(__name__)
SESSION = requests.Session()
API_ENDPOINT = 'https://en.wikipedia.org/w/api.php'
@APP.route('/', methods=['GET', 'POST'])
defindex():
""" 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')
deffetch_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

[edit ]

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){
varplaces=response["results"],
no_thumb="..";
x.innerHTML="";

for(varpinplaces){
varthumbnail=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(){
varx=document.getElementById("places-list");

$(".btn-search").click(function(){
getLocation();
});
functiongetLocation(){
x.innerHTML="Searching your location..";
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(fetchPlaces);
}else{
x.innerHTML="Geolocation is not supported by this browser.";
}
}
functionfetchPlaces(position){
vardata={
"latitude":position.coords.latitude,
"longitude":position.coords.longitude
};
$.ajax({
url:"/",
type:"POST",
data:JSON.stringify(data),
contentType:"application/json",
dataType:"json",
success:function(response){
varplaces=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(varpinplaces){
varthumbnail=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

[edit ]

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:10px25px;
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.itemp,
span{
font-size:12px;
margin:2px;
}
.places-container.itemspan{
color:gray;
}
.places-container.itemimg{
float:right;
width:80px;
height:80px;
border-radius:5px;
object-fit:cover;
}
.places-container.itema{
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>
Nearby places viewer demo app screenshot (2)

Next steps

[edit ]

See also

[edit ]

AltStyle によって変換されたページ (->オリジナル) /