Munstadi

API, Development, Front-End
Contractor: H1 Web Oy
Client: Munstadi
Website: munstadi.fi
Close X

As part of a bigger project for Munstadi I had to implement a map displaying places based on coordinates fetched from an API provided by the city of Helsinki.

I decided to go with the Open Source project Leaflet.js for the front-end development and with writing a plugin for the back-end.

The communication with the API happens in the client side and it’s triggered whenever a new place is created (a subsite in the same network in this case) or whenever the user clicks the ‘Regenerate all coordinates’ button.
 

munstadi1

munstadi2

/**
 * Map API WordPress Plugin
 * Author: Federico Di Rosa
 * Version: 1.0
 * License: MIT
 */

if (!class_exists('MapApiPublic')) {

	class MapApiPublic {

		public static $map_div_id = 'map';

		public function __construct() {}

		public function get_all_places_data() {
			$coordinates = array();
			$args = array(
				'post_type'  => MapApiAdmin::$post_type,
				'posts_per_page' => -1,
				'meta_query' => array(
					array(
						'key'     => MapApiAdmin::$latitude_meta_key,
						'value' => '',
						'compare' => '!='
					),
					array(
						'key'     => MapApiAdmin::$longitude_meta_key,
						'value' => '',
						'compare' => '!='
					)
				),
			);

			$all_coordinates = new WP_Query( $args );
			if ( $all_coordinates->have_posts() ) :
				while ( $all_coordinates->have_posts() ) :
					$all_coordinates->the_post();
					$coordinates[ get_the_id() ]['lat'] = get_post_meta( get_the_id(), MapApiAdmin::$latitude_meta_key, true );
					$coordinates[ get_the_id() ]['lon'] = get_post_meta( get_the_id(), MapApiAdmin::$longitude_meta_key, true );
					$coordinates[ get_the_id() ]['title'] = get_the_title();
					$coordinates[ get_the_id() ]['url'] = get_post_meta( get_the_id(), MapApiAdmin::$site_url_meta_key, true );
				endwhile;
			endif;
			wp_reset_postdata();

			return $coordinates;
		}

		public function load_assets() {
			wp_enqueue_script( 'map-api-leaflet-js', plugin_dir_url( MAP_API_BASENAME ) . 'vendor/leaflet-0.7.3/leaflet.js', false, '1.0', true );
			wp_enqueue_script( 'map-api-leaflet-providers-js', plugin_dir_url( MAP_API_BASENAME ) . 'vendor/leaflet-plugins/leaflet-providers.js', array( 'map-api-leaflet-js' ), '1.0', true );
			wp_enqueue_style( 'map-api-leaflet-css', plugin_dir_url( MAP_API_BASENAME ) . 'vendor/leaflet-0.7.3/leaflet.css', false, '1.0' );
			wp_register_script( 'map-api-js', plugin_dir_url( MAP_API_BASENAME ) . 'js/map-api.min.js', array( 'jquery', 'map-api-leaflet-js' ), '1.0', true );
			wp_enqueue_style( 'map-api-css', plugin_dir_url( MAP_API_BASENAME ) . 'css/map-api.css', false, '1.0' );

			$data = array(
				'map_div_id' => self::$map_div_id,
				'coordinates' => self::get_all_places_data()
			);

			wp_localize_script( 'map-api-js', 'map_api_data', $data );
			wp_enqueue_script( 'map-api-js' );
		}

		public static function render_map() {
			self::load_assets();
			echo self::$map_div_id;
		}

	}

}

 

/*!
 * Map API WordPress Plugin
 * Author: Federico Di Rosa
 * Version: 1.0
 * License: MIT
 */

jQuery(document).ready(function($) {

	var MAP_API = {
		map: '',
		init: function(){
			if (MAP_API.is_map())
				MAP_API.create_map();
		},
		is_map: function() {
			if (!$('#' + map_api_data.map_div_id).length) return false;
			else return true;
		},
		create_map: function(){
			MAP_API.map = L.map( map_api_data.map_div_id, {
			    center: [60.1708, 24.9375], // default: Helsinki
			    zoom: 12,
			    attributionControl: false
			});
			MAP_API.tile_layer();
			MAP_API.add_markers();
		},
		tile_layer: function(){
			var topLayer = L.tileLayer.provider('Hydda.Full').addTo( MAP_API.map ); // OpenStreetMap.HOT is another good one
		},
		add_markers: function(){
			var coordinates = [];
			var fit_all_markers = true;
			$.each(map_api_data.coordinates, function(i, post_id) {
				if (!isNaN(post_id.lat) && !isNaN(post_id.lon)) {
					var marker = L.marker([ post_id.lat, post_id.lon ]).addTo( MAP_API.map ).bindPopup( '<div class="place-title">' + post_id.title + '</div>' + post_id.url + '' );
					var place = [];
					var match = false;
					if ( parseInt(i) === parseInt(MAP_API.get_request_id()) ) {
						match = true;
						coordinates = [];
						marker.openPopup();
					}
					if (fit_all_markers === true) {
						place.push(post_id.lat);
						place.push(post_id.lon);
						coordinates.push(place);
					}
					if (match === true) {
						fit_all_markers = false;
					}
				}
			});
			MAP_API.set_view_with_bounds( coordinates );
		},
		set_view_with_bounds: function() {
			var bounds = L.latLngBounds(arguments[0]);
			MAP_API.map.fitBounds(bounds);
		},
		get_request_id: function() {
			var search = window.location.search;
			var request_id = search.substr(search.indexOf('&id=') + 4);
			return request_id;
		}
	};

	MAP_API.init();

});

 

/*!
 * Map API WordPress Plugin
 * Author: Federico Di Rosa
 * Version: 1.0
 * License: MIT
 */

jQuery(document).ready(function($) {

	var MAP_API_ADMIN = {
		error_msg: 'There was an error fetching the data, try again later<br><br><div class="map-api-close button button-primary button-large">OK</div>',
		saving_msg: 'Saving coordinates..<br><br><div class="map-api-close button button-primary button-large">OK</div>',
		success_msg: 'Coordinates saved successfully<br><br><div class="map-api-close button button-primary button-large">OK</div>',
		error_saving: 'There was a problem saving the data, try again<br><br><div class="map-api-close button button-primary button-large">OK</div>',
		not_found_msg: 'Site\'s url not found in City of Helsinki Service Map API<br><br>Insert coordinates manually (leave empty to skip):<br><input type="text" style="width: 80px; margin-top: 6px;" id="latitude_manual" placeholder="Latitude">&nbsp;<input type="text" style="width: 80px; margin-top: 6px;" id="longitude_manual" placeholder="Longitude"><br><br><div class="map-api-close submit-coordinates-manually button button-primary button-large">OK</div>',
		not_found_msg_all: 'None of the sites were found in City of Helsinki Service Map API<br><br><div class="map-api-close button button-primary button-large">OK</div>',
		fetching_msg: 'Fetching coordinates from City of Helsinki Service Map API<div class="loading-dots" style="width: 16px; display: inline-block; text-align: left;"></div><br>(Please wait, this may take few minutes)<br><br><div class="map-api-close abort-ajax button button-primary button-large">STOP</div>',
		request: false,
		not_found: false,
		init: function() {
			if (map_api_admin_data.regenerate_all == 1) {
				MAP_API_ADMIN.regenerate_all();
			} else {
				if (map_api_admin_data.new_site == 1) {
					MAP_API_ADMIN.wait_for_it();
				} else {
					MAP_API_ADMIN.initiate_call();
				}
			}
		},
		wait_for_it: function() {
			$(document).ajaxComplete(function( event, xhr, settings ) {
				if ( settings.url == '/wp-admin/admin-ajax.php' ) {
					if (map_api_admin_data.new_site == 1) {
						MAP_API_ADMIN.initiate_call();
						map_api_admin_data.new_site = false;
					}
				}
			});
		},
		regenerate_all: function() {
			$('input[type="button"]#regenerate-all').on('click', function(){
				MAP_API_ADMIN.initiate_call();
			});
		},
		initiate_call: function() {
			MAP_API_ADMIN.show_message( MAP_API_ADMIN.fetching_msg );
			MAP_API_ADMIN.api_call();
		},
		loading_dots: function() {
			var dots = 0;
			if (!$('.loading-dots').length)
				return;
			setInterval (function() {
				if (dots < 3) {
					$('.loading-dots').append('.');
					dots++;
				} else {
					$('.loading-dots').text('');
					dots = 0;
				}
			}, 500);
		},
		api_call: function() {
			MAP_API_ADMIN.request = $.ajax({
				url: map_api_admin_data.api_url,
				type: 'GET',
				dataType: 'jsonp',
				// xhrFields: {
				// 	onprogress: function (e) {
				// 		if (e.lengthComputable) {
				// 			console.log(e.loaded / e.total * 100 + '%');
				// 		}
				// 	}
				// }
			});

			MAP_API_ADMIN.request.done(function( data, textStatus, jqXHR ) {
				MAP_API_ADMIN.process_data( data );
			});

			MAP_API_ADMIN.request.fail(function( jqXHR, textStatus, errorThrown ) {
				if ($('.map-api-modal-inner-div').length)
					$('.map-api-modal-inner-div').html( MAP_API_ADMIN.error_msg );
			});
		},
		parse_url: function( url ) {
			url = url.toLowerCase();
			var url_object = document.createElement('a');
			url_object.href = url;
			var hostname = url_object.hostname;
			var pathname = url_object.pathname;
			var search = url_object.search;
			url = hostname + pathname + search;
			if (url.substr(-1) == '/') {
			    url = url.substr(0, url.length - 1);
			}
			return url;
		},
		compare_urls: function( url1, url2 ) {
			if (url1 === undefined || url2 === undefined)
				return false;

			url1 = MAP_API_ADMIN.parse_url( url1 );
			url2 = MAP_API_ADMIN.parse_url( url2 );

			if (url1 == url2)
				return true;
			else
				return false;
		},
		process_data: function( data ) {
			var coordinates = {};
			if (map_api_admin_data.regenerate_all == 1) {
				if (map_api_admin_data.posts) {
					MAP_API_ADMIN.not_found = true;
					$.each(map_api_admin_data.posts, function(post_id, site_url) {
						if ($.isArray(data)) {
							$.each(data, function(index, object) {
								var returned_value = MAP_API_ADMIN.get_coordinates( object, site_url );
								if (returned_value) coordinates[post_id] = returned_value;
							});
						} else {
							coordinates[post_id] = MAP_API_ADMIN.get_coordinates( data, site_url );
						}
					});
					if ( !MAP_API_ADMIN.not_found ) {
						MAP_API_ADMIN.regenerate_all_update_post_meta( coordinates );
						if ($('.map-api-modal-inner-div').length)
							$('.map-api-modal-inner-div').html( MAP_API_ADMIN.saving_msg );
					} else {
						if ($('.map-api-modal-inner-div').length)
							$('.map-api-modal-inner-div').html( MAP_API_ADMIN.not_found_msg_all );
					}
				}
			} else {
				MAP_API_ADMIN.not_found = true;
				if ($.isArray(data)) {
					$.each(data, function(index, object) {
						var returned_value = MAP_API_ADMIN.get_coordinates( object, map_api_admin_data.site_url );
						if (returned_value) coordinates[post_id] = returned_value;
					});
				} else {
					coordinates[post_id] = MAP_API_ADMIN.get_coordinates( data, map_api_admin_data.site_url );
				}
				if ( !MAP_API_ADMIN.not_found ) {
					MAP_API_ADMIN.update_post_meta( coordinates );
					if ($('.map-api-modal-inner-div').length)
						$('.map-api-modal-inner-div').html( MAP_API_ADMIN.saving_msg );
				} else {
					if ($('.map-api-modal-inner-div').length)
						$('.map-api-modal-inner-div').html( MAP_API_ADMIN.not_found_msg );
				}
			}
		},
		get_coordinates: function( data, site_url ) {
			var coordinates;
			if (data.hasOwnProperty('www_fi') && MAP_API_ADMIN.compare_urls( data.www_fi, site_url )) {
				if (data.hasOwnProperty('latitude') && data.hasOwnProperty('longitude')) {
					coordinates = {};
					coordinates.latitude = data.latitude;
					coordinates.longitude = data.longitude;
					MAP_API_ADMIN.not_found = false;
				}
			}
			return coordinates;
		},
		update_post_meta: function( coordinates ) {
			MAP_API_ADMIN.request = $.ajax({
				url: map_api_admin_data.ajax_url,
				type: 'POST',
				dataType: 'html',
				data: {
					action: 'update_post_meta_coordinates',
					latitude: coordinates.latitude,
					longitude: coordinates.longitude
				}
			});

			MAP_API_ADMIN.request.done(function( data, textStatus, jqXHR ) {
				if ($('.map-api-modal-inner-div').length)
					$('.map-api-modal-inner-div').html( MAP_API_ADMIN.success_msg );
			});

			MAP_API_ADMIN.request.fail(function( jqXHR, textStatus, errorThrown ) {
				if ($('.map-api-modal-inner-div').length)
					$('.map-api-modal-inner-div').html( MAP_API_ADMIN.error_saving );
			});
		},
		regenerate_all_update_post_meta: function( coordinates ) {
			MAP_API_ADMIN.request = $.ajax({
				url: map_api_admin_data.ajax_url,
				type: 'POST',
				dataType: 'html',
				data: {
					action: 'regenerate_all_update_post_meta_coordinates',
					coordinates: coordinates
				}
			});

			MAP_API_ADMIN.request.done(function( data, textStatus, jqXHR ) {
				if ($('.map-api-modal-inner-div').length)
					$('.map-api-modal-inner-div').html( MAP_API_ADMIN.success_msg );
			});

			MAP_API_ADMIN.request.fail(function( jqXHR, textStatus, errorThrown ) {
				if ($('.map-api-modal-inner-div').length)
					$('.map-api-modal-inner-div').html( MAP_API_ADMIN.error_saving );
			});
		},
		remove_message: function() {
			if ($('.map-api-modal-overlay').length) {
				$('.map-api-modal-overlay').remove();
			}
		},
		close_modal: function() {
			$(document).on('click', '.map-api-close', function(){
				if ($(this).hasClass('submit-coordinates-manually')) {
					if ($('input#latitude_manual').val() !== '' && $('input#longitude_manual').val() !== '') {
						MAP_API_ADMIN.update_post_meta( { latitude: $('input#latitude_manual').val(), longitude: $('input#longitude_manual').val() } );
					} else {
						MAP_API_ADMIN.remove_message();
					}
				} else if ($(this).hasClass('abort-ajax')) {
					MAP_API_ADMIN.request.abort();
				} else {
					MAP_API_ADMIN.remove_message();
				}
			});
		},
		show_message: function( message ) {
			var max_width = 500;
			var max_height = 250;
			var window_width = $(window).width() - 40;
			var window_height = $(window).height() - 40;
			var inner_div_height = $('.map-api-modal-inner-div').height();
			if (window_width > max_width) window_width = max_width;
			if (window_height > max_height) window_height = max_height;
			var overlay_style = 'position:fixed;';
			overlay_style += ' top:0;';
			overlay_style += ' right:0;';
			overlay_style += ' bottom:0;';
			overlay_style += ' left:0;';
			overlay_style += ' background:rgba(0,0,0,0.5);';
			overlay_style += ' z-index:9999999;';
			var modal_style = 'position:absolute;';
			modal_style += ' width:' + window_width + 'px;';
			modal_style += ' left:50%;';
			modal_style += ' margin-left:-' + (window_width/2) + 'px;';
			modal_style += ' height:' + window_height + 'px;';
			modal_style += ' top:50%;';
			modal_style += ' margin-top:-' + (window_height/2) + 'px;';
			modal_style += ' background:white;';
			modal_style += ' color:#333;';
			modal_style += ' text-align:center;';
			var inner_div_style = 'padding: 0px 40px;';
			inner_div_style += ' -o-transform: translateY(-50%);-moz-transform: translateY(-50%);-webkit-transform: translateY(-50%);-ms-transform: translateY(-50%);transform: translateY(-50%);';
			inner_div_style += ' position: relative;';
			inner_div_style += ' top: 50%;';
			$('body').append('<div class="map-api-modal-overlay" style="' + overlay_style + '"><div class="map-api-modal" style="' + modal_style + '"><div class="map-api-modal-inner-div" style="' + inner_div_style + '">' + message + '</div></div></div>');
			MAP_API_ADMIN.loading_dots();
			MAP_API_ADMIN.close_modal();
		}
	};

	MAP_API_ADMIN.init();

});