Wiki

Information and Resources

User Tools

Site Tools


api:query_keyword

Simple JournalMap API Example - Keyword Search

This page goes through a simple example of running a keyword search via the JournalMap API and displaying the points on a Leaflet map. Make sure to check out the JournalMap API documentation for all the specifics on working with the API. Note that you'll need to get an API key to make this example work. Also, JournalMap's API paginates responses into groups of 30. This example does handle API pagination for a collection with more than 30 articles. This example uses D3.js, Leaflet, and the Leaflet Marker Cluster plugin. It also uses queue.js to hold processing of the articles until all the pages have been loaded, and spin.js for a nifty spinning wheel icon while the map loads.

Example of an Embedded Collection Map via the JournalMap API

JournalMap API keyword search for "Migratory Connectivity"


Steps for creating the example

To see the full script put together, see our example on GitHub.

1. Add the script references to the page header

Add the following references to the <head> section of your webpage:

	<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
	<script src="http://d3js.org/queue.v1.min.js"></script>
	<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
	<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
	<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.css" />
	<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.Default.css" />
	<script src="http://leaflet.github.io/Leaflet.markercluster/dist/leaflet.markercluster-src.js"></script>
        <script src="http://fgnass.github.io/spin.js/spin.js"></script>

2. Add a container to hold the map

Put the HTML <div> tags in the body of your page where you want your map to be. For this example I added a couple of <div> tags before the map for the collection title, intro, and description. Note the ids of each of these that are used to identify them later on and put the right text in them. For the map, I added a <div> tag and gave it an id=“jmap”. Finally, I added a footer with a nice little JournalMap logo. I wrapped my example in a container <div> and created some simple CSS styling for good measure.

 <style>
	.spinner {
		position: relative;
		top: 50%;
		left: 10%;
	}
 
	.title {
		border: none;
  		font: normal normal bold 22px/1 Georgia, serif;
  		color: black;
  		text-align: center;
  		margin: 0 0 6px;
		padding: 4px;
	}
 
	.container {
		padding:8px;
		margin: 3px;
		width:600px;
		border:thin solid black;
		box-shadow: 5px 5px 2px #888888
	}
</style>
 
 
 
<h2>Example of a Keyword Search Map via the JournalMap API</h2>
 
  <div class="container">
 	<h3 id="mapName" class="title">JournalMap API keyword search for "Migratory Connectivity"</h3>
	<div id="jmap" style="height:400px;" >
		<div class="spinner" id="jmapSpin"></div>
	</div>
	<a href="https://www.journalmap.org"><img src="http://wiki.journalmap.org/lib/exe/fetch.php?media=jmap_logo.png" width="150px"></a>
  </div>    

3. Add a script block for the map coding

Add a <script> tag after your <div> container. In the script tag, we'll set some of the initial values that we'll use in the map. You'll set the keywords that you want to search from JournalMap, the coordinate of where you want the map centered, a map zoom level, and your API key. We'll also set up the parameters for the progress spinning wheel.

	var keywords = "Migratory Connectivity";
	var centerLat = 40;
	var centerLon = -100;
	var Zoom = 3;
	var apikey = "PjFRefUzFYzFCvshie8Q";
	var data = []; // a global to store the json response
 
	var spinner = new Spinner({
		lines: 12, // The number of lines to draw
		length: 7, // The length of each line
		width: 3, // The line thickness
		radius: 10, // The radius of the inner circle
		color: '#000', // #rbg or #rrggbb
		speed: 1, // Rounds per second
		trail: 100, // Afterglow percentage
		shadow: false // Whether to render a shadow
	})

The API will return all the parts of a citation, but as separate data fields. So you'll need some functions to assemble the pieces into a decent looking citation and construct links from the DOI numbers.

	String.prototype.trimRight = function(charlist) {
  			if (charlist === undefined)
			    charlist = "\s";
			return this.replace(new RegExp("[" + charlist + "]+$"), "");
		};
 
		// Function to build citations from JournalMap database fields
	var buildCitation = function(title,authors,year,journal,vol,iss,spage,epage) {
		authorlist = '';
		for (i = 0; i < authors.length; i++) {
			if (i===0) {
				var sep = '';
			} else if (i===(authors.length-1)) {
				sep = ' and ';
			} else {
				sep = ', ';
			}
			authorlist += sep+authors[i];
		}
		var vips = '';
		if (vol!='') { vips+=vol; }
		if (iss!='') { vips+='('+iss+')'; }
		if (vips!='') { vips+=':'; }
		if (spage!='') { vips+=spage; }
		if (epage!='') { vips+='-'+epage; }
		return(authorlist.trimRight('.')+'. '+year+'. '+title.trimRight('.')+'. '+'<em>'+journal.trimRight('.')+'</em> '+vips);
	};
 
	// Function to build HTML link from DOI numbers
	var buildDOIlink = function(doi) {
		return('<a href="http://dx.doi.org/'+doi+'" target="_blank">'+doi+'</a>');
	};
 
 
	// function to build HTML for the popup box for each marker
	var buildPopupHTML = function(id,title,citation,doi,url) {
		var html = "<strong><a href='https://www.journalmap.org/articles/"+id+"/' target='_blank'>"+
			title+"</a></strong><p>"+citation+"<p><strong>DOI: </strong>"+buildDOIlink(doi)+
			"<br><strong>URL: </strong>"+url;
		return html;
	};

5. Fetch the articles from the JournalMap API

Now we call the API again to get the articles. Once we've iterated over all the articles, we'll add the marker layer to the map. This time we're using D3's XHR method to return the article data. The reason for this has to do with the API pagination - the pagination information we need is contained in the response headers from the API and we can't get to those from the d3.json function. Because JavaScript is asynchronous, it will keep going and execute subsequent code even while it's waiting to hear back from the API. Normally this isn't an issue, but when we need to iterate over many pages it creates a problem. The solution here is to use queue.js to make JavaScript wait until all the pages from the API are loaded before going on. The output from the q.defer statement is a list of json “files” that need to be appended together into a single dataset we can use for our map. Also, note that I've left the d3.xhr statement open here. We'll need to add our code for building the citation and map markers in here before we close it.

// Fetch the articles in the collection and build the points.
		d3.xhr("https://www.journalmap.org/api/articles.json?key="+apikey+"&page=1&version=1.0&query="+keywords, function(error,pubjson) {
			var pages = pubjson.getResponseHeader('X-Pages');
			spinner.spin(document.getElementById("jmapSpin"));
			var articles = [];
			q = queue();
			for(page=1;page<=pages;page++) {
				q.defer(d3.json, "https://www.journalmap.org/api/articles.json?key="+apikey+"&page=1&version=1.0&query="+keywords)
			}
			q.awaitAll(function(error,files) { 
 
				for (f=0;f<files.length;f++) { 
					articles.push.apply(articles,files[f]);
				};

6. Setup the Leaflet map

The next step is to initialize the Leaflet map and set some of its basic properties. We'll create a marker cluster group to hold the points that we'll generate next.

		// Initialize the map
		var map = L.map('jmap').setView([centerLat, centerLon], Zoom);
		L.tileLayer('http://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png', {
		   maxZoom: 18,
		}).addTo(map);
		var markers = L.markerClusterGroup();

7. Get article info and build the map markers

For this step we iterate through all the articles in the collection (articles.forEach statement). For each article, first we assemble the author list and then build the citation. Then we loop through all of the article's locations and create a new map marker. We use our buildPopupHTML function to create the content for our popups. Finally we add the markers to the map and stop the spinning wheel. Don't forget to close out the q.awaitAll and d3.xhr functions that we left open.

				// Assemble the markers for the map
				markers = L.markerClusterGroup();  // Empty out the markers if they exist already
				articles.forEach(function(article){
					var authorlist = []
					article.authors.forEach(function(author) {
						authorlist.push(author.last_name + ", " + author.first_name);
					});
    				var citation = buildCitation(article.title,authorlist,article.publish_year,article.publication.name,article.volume,article.issue,article.start_page,article.end_page);
					article.locations.forEach(function(location){
						var marker = L.marker([location.latitude,location.longitude]);
						marker.bindPopup(buildPopupHTML(article.id,article.title,citation,article.doi,article.url)); 
    					markers.addLayer(marker);	
					});
				});
				map.addLayer(markers);
 
				spinner.stop(document.getElementById("jmapSpin"));
 
			});  // Close the q.awaitAll
		});  // Close the d3.xhr call

9. Add map attribution

Finally, we'll add some attribution lines to the map. Lastly, we need to close out the original d3.json that we left open.

		// Set the map source attribution
  		maplink = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
		landlink = '<a href="http://thunderforest.com/">Thunderforest</a>'; 
		cclink = '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>';
		leaflink = '<a href="http://leafletjs.com">Leaflet</a>';
		map.attributionControl.addAttribution('Maps &copy; '+landlink+', Data &copy; '+maplink+', '+cclink+' | <a href="https://www.journalmap.org">JournalMap</a> data provided CC-BY-SA');
 
	});   	// Close the original d3.json that fetched the collection info.  
api/query_keyword.txt · Last modified: 2015/01/23 22:02 by jkarl

FEEDBACK    DOWNLOADS    ABOUT    SUPPORT US    HELP    BLOG    WIKI

JournalMap data is licensed under a
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
Creative Commons License