PHP Classes

File: public/assets/local/js/openlayers.min.js

Recommend this page to a friend!
  Classes of Aby Dahana  >  Aksara  >  public/assets/local/js/openlayers.min.js  >  Download  
File: public/assets/local/js/openlayers.min.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Aksara
A CodeIgniter based API and CRUD generator
Author: By
Last change: Initial commit for update 4.2.8

1. Core improvements;
2. Menu generator improvements;
3. Validation for file upload improvements;
4. Adds the failed login attempts blocking with customizable attempt count and blocking time;
5. Login page improvements, removing the login component due to the easy customization;
6. Removing unused JS plugins;
7. Routing machine preparation for maps rendering;
8. Preparing the market addons to be able to auto update the installed modules and themes;
9. Changes for uploader plugin with Aksara custom uploader since the previous version used to be paid version (fileuploader by innostudio);
10. Improve the carousel slideshow CMS that previously missing the order when uploaded file is skipping the first slide;
Initial commit for update 4.2.8

- Add the capability to get nearest features when using OpenLayers;
- Refine the core module views.
Initial commit for update 4.2.8
Initial commit for update 4.2.8

Change the phrase
Initial commit for update 4.1.8
Initial commit for update 4.2.8
Initial commit for update version 4.2.8

1. \aksara\Laboratory\Core.php: assign the method of "parent_module" related to view template and permission, add the option parameter for "set_title" to set the individual title for current action (index, create, read, update), improve the autocomplete function;

2. \aksara\Laboratory\Model.php: add the method to get error message, throwing an error message than error exception;

3. \aksara\Laboratory\Permission.php: change the thrown error method when validate AJAX request;

4. \aksara\Laboratory\Template.php: correct the view path finder of sub-modules;

5. Change the template of core modules;

6. Add the reverse geocoding for openlayers when updating the layer feature;

7. Minor updates for core plugins.
Revert "Initial commit for updat 4.2.8"

This reverts commit 5d2eaabba9d42ad8dc108eaf4303b2d58a50a735.
Initial commit for update 3.2.8

1. \aksara\Laboratory\Core.php: assign the method of "parent_module" related to view template and permission, add the option parameter for "set_title" to set the individual title for current action (index, create, read, update), improve the autocomplete function;

2. \aksara\Laboratory\Model.php: add the method to get error message, throwing an error message than error exception;

3. \aksara\Laboratory\Permission.php: change the thrown error method when validate AJAX request;

4. \aksara\Laboratory\Template.php: correct the view path finder of sub-modules;

5. Change the template of core modules;

6. Add the reverse geocoding for openlayers when updating the layer feature;

7. Minor updates for core plugins.
Revert "Revert "Revert "Initial commit for updat 4.2.8"""

This reverts commit 73226fbaf324b253ed2c3888e8eabd81ca8ef946.
Revert "Revert "Initial commit for updat 4.2.8""

This reverts commit 94a714c958f637d932a6bea4bb15a884ddef733e.
Revert "Initial commit for updat 4.2.8"

This reverts commit 5d2eaabba9d42ad8dc108eaf4303b2d58a50a735.
Revert "Revert "Initial commit for updat 4.2.8""

This reverts commit 91ca274f4924f2ba97344ab5d80cb64f6a1a1274.
Revert "Initial commit for updat 4.2.8"

This reverts commit 5d2eaabba9d42ad8dc108eaf4303b2d58a50a735.
Initial commit for updat 4.2.8

1. \aksara\Laboratory\Core.php: assign the method of "parent_module" related to view template and permission, add the option parameter for "set_title" to set the individual title for current action (index, create, read, update), improve the autocomplete function;

2. \aksara\Laboratory\Model.php: add the method to get error message, throwing an error message than error exception;

3. \aksara\Laboratory\Permission.php: change the thrown error method when validate AJAX request;

4. \aksara\Laboratory\Template.php: correct the view path finder of sub-module;

5. Change the template of core module;

6. Add the reverse geocoding for openlayers when updating the layer feature;

7. Minor update for core plugins.
Initial commit for update 4.2.8

1. Updating the \aksara\Laboratory\Core.php and adds the hyperlink to rendered view (set_field);
2. Upating the \aksara\Laboratory\Template.php and add the statement to hide the icon (that sets from menu management) when using blank icon;
3. Add the ArcGIS MapServer function rendering to \public\assets\local\js\openlayers.min.js when display the map using "data-arcgis-rest-url" data attribute;
4. Adding the ripple effect to the button.
Update openlayers.min.js
Update missing partial statement

1. Update OpenLayer geometry checking;
2. Adding new phrase for installation;
3. Updating missing requirement notice.
Date: 5 days ago
Size: 53,064 bytes
 

Contents

Class file image Download
/**
 * OpenLayers Map Module
 * This module will initialize map from the given parameter
 *
 * @author			Aby Dahana
 * @profile			abydahana.github.io
 *
 * Property of Aksara Laboratory
 * www.aksaracms.com
 */

"use strict";

/**
 * default variables
 */
var _this,
	xhr,
	map,
	layerVector,
	layerOverlap,
	clickedPoint,
	measurementVector,
	selected,
	selectionBox,
	drawingManager,
	drawingType,
	draggableMarker,
	lngLat,
	disable_default_marker,
	colorscheme,
	fill_color,
	stroke_color,
	icon_pattern,
	geocoder,
	geolocation,
	apply_coordinate,
	apply_address,
	apply_measure_area,
	apply_measure_distance,
	arcgis_rest_url,
	geojson,
	c,
	originalHandleEvent,
	highlighted,
	dragging,
	maxZoom											= 20,
	features										= [],
	projection										= 'EPSG:4326',
	popup											= new ol.Overlay.Popup(),
	highlight										= new ol.interaction.Select(),
	tileSource										= new ol.source.XYZ
	({
		/* Google Maps will be used, comment the url and attributions to rollback the default */
		url: (config.default_map_tile ? config.default_map_tile : 'https://mt{0-3}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}'),
		attributions:
		[
			' <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a> contributors.'
		],
		crossOrigin: 'anonymous'
	});

var openlayers										= (function()
{
	return {
		/**
		 * render the map
		 */
		render: function(_this_)
		{
			this.reset();
			
			/* keep the "this" context */
			_this									= _this_,
			apply_coordinate						= _this.attr('data-apply-coordinate-to'),
			apply_address							= _this.attr('data-apply-address-to'),
			apply_measure_area						= _this.attr('data-apply-measure-area-to'),
			apply_measure_distance					= _this.attr('data-apply-measure-distance-to'),
			disable_default_marker					= (1 == _this.attr('data-disable-default-marker') ? true : false),
			
			arcgis_rest_url							= _this.attr('data-arcgis-rest-url'),
			
			fill_color								= (_this.attr('data-fill') ? _this.attr('data-fill') : null),
			stroke_color							= (_this.attr('data-stroke') ? _this.attr('data-stroke') : null),
			icon_pattern							= (_this.attr('data-icon') ? _this.attr('data-icon') : null),
			
			leave_page								= false,
			
			/* set the coordinate from data-coordinate attribute*/
			lngLat									= (_this.attr('data-coordinate') ? JSON.parse(_this.attr('data-coordinate')) : (_this.attr('data-map-center') ? JSON.parse(_this.attr('data-map-center')) : [])),
			lngLat									= (lngLat && (typeof lngLat.lng !== 'undefined' && typeof lngLat.lat !== 'undefined' ? [lngLat.lng, lngLat.lat] : (typeof lngLat[0] !== 'undefined' && typeof lngLat[1] !== 'undefined' ? [lngLat[0], lngLat[1]] : [107.0680127, -6.2299611]))),
			colorscheme								= (typeof lngLat.colorscheme !== 'undefined' ? lngLat.colorscheme : '#ff0000');
			
			if(!_this.attr('id'))
			{
				_this.attr('id', 'maps')
			}
			
			if(map)
			{
				map.dispose()
			}
			
			/* define and render map */
			map										= new ol.Map
			({
				interactions: ol.interaction.defaults
				({
					mouseWheelZoom: false,
					dragPan: true
				})
				.extend
				([
					new ol.interaction.MouseWheelZoom
					({
						condition: function(e)
						{
							return (0 != _this.attr('data-mousewheel') || ol.events.condition.platformModifierKeyOnly(e));
						}
					})
				]),
				target: _this.attr('id'),
				layers:
				[
					new ol.layer.Tile
					({
						/*source: new ol.source.OSM()*/
						source: tileSource
					})
				],
				view: new ol.View
				({
					center: ol.proj.fromLonLat(lngLat),
					zoom: (_this.attr('data-zoom') ? parseInt(_this.attr('data-zoom')) : 12),
					maxZoom: maxZoom
				}),
				loadTilesWhileAnimating: false,
				loadTilesWhileInteracting: false
			});
			
			var resolution							= map.getView().getResolution();
			
			/*
			openlayers.routing([106.848942,-6.206094], [106.983602,-6.244223]),
			*/
			
			/* add fullscreen control */
			(_this.attr('control-fullscreen') && $(window).outerWidth() > 1024 ? map.addControl(new ol.control.FullScreen()) : ''),
			
			/* add scaleline control */
			(_this.attr('control-scaleline') ? map.addControl(new ol.control.ScaleLine()) : ''),
			
			/* add mouseposition control */
			(_this.attr('control-mouseposition') ? map.addControl(new ol.control.MousePosition({coordinateFormat: ol.coordinate.createStringXY(5), projection: projection, prefix: 'Degrees', undefinedHTML: '&nbsp;'})) : ''),
			
			/* add zoom extent control */
			(_this.attr('control-zoom-extent') ? map.addControl(new ol.control.ZoomToExtent({extent: map.getView().calculateExtent()})) : ''),
			
			$('.ol-zoom-extent').children('button').html('<i class="mdi mdi-home"></i>'),
			
			$('.ol-zoom-extent').on('click', function(){map.getView().fit(map.getView().calculateExtent(), {size: map.getSize()}), map.getView().setResolution(resolution)});
			
			if(_this.attr('data-drawing-manager'))
			{
				/* change map tiles to satellite */
				tileSource.setUrl((config.default_map_tile ? config.default_map_tile : 'https://mt{0-3}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}'))
			}
			else
			{
				/* add default popup */
				map.addOverlay(popup),
				
				/* add highlight interaction */
				map.addInteraction(highlight),
				
				/* add dot to clicked map */
				map.on('singleclick', function(event)
				{
					if(clickedPoint && clickedPoint.getSource().getFeatures().length)
					{
						clickedPoint.getSource().getFeatures()[0].setGeometry(new ol.geom.Point(event.coordinate))
					}
					else
					{
						clickedPoint				= new ol.layer.Vector
						({
							source: new ol.source.Vector
							({
								features:
								[
									new ol.Feature
									({
										geometry: new ol.geom.Point(event.coordinate)
									})
								]
							}),
							style: new ol.style.Style
							({
								image: new ol.style.Circle
								({
									radius: 5,
									stroke: new ol.style.Stroke
									({
										color: '#880000',
										width: 1
									}),
									fill: new ol.style.Fill
									({
										color: '#ff0000'
									})
								})
							}),
							initial: 'clickedPoint'
						});
						
						map.addLayer(clickedPoint)
					}
				})
			}
			
			/* on property change */
			map.getView().on('propertychange', function(event)
			{
				if(typeof geolocation !== 'undefined')
				{
					/* stop tracking to prevent map flicker */
					geolocation.setTracking(false)
				}
			}),
			
			/* on pointermove */
			map.on('pointermove', function(e)
			{
				var pixel							= map.getEventPixel(e.originalEvent),
					hit								= map.hasFeatureAtPixel(pixel);
					
				map.getViewport().style.cursor = hit ? 'pointer' : '';
			}),
			
			/* on zoom */
			map.on('moveend', function(e)
			{
				if(_this.attr('data-clustering-url'))
				{
					/*
					xhr								= $.ajax
					({
						url: _this.attr('data-clustering-url'),
						method: 'POST',
						data:
						{
							extent: ol.proj.transformExtent(map.getView().calculateExtent(map.getSize()), map.getView().getProjection(), projection)
						},
						beforeSend: function()
						{
							if(xhr)
							{
								xhr.abort()
							}
						}
					})
					.done(function(response)
					{
						openlayers.unzip({url: response.url, initial: response.initial})
					})
					*/
				}
			}),
			
			/* update map size */
			map.updateSize(),
			
			(_this.attr('data-coordinate') ? this.coordinate((1 == _this.attr('data-draggable') ? true : false)) : null),
			
			(_this.attr('data-geocoder') ? this.geocoder(_this) : null),
			
			(_this.attr('data-geolocation') ? this.geolocation() : null),
			
			(_this.attr('data-geojson') ? this.geojsonString(JSON.parse(_this.attr('data-geojson')), _this.attr('data-drawing-type')) : null),
			
			(_this.attr('data-geojson-url') ? this.geojson(_this.attr('data-geojson-url'), _this.attr('data-drawing-type')) : null),
			
			(_this.attr('data-arcgis-rest-url') ? this.arcgis(arcgis_rest_url) : null)
		},
		
		/**
		 * reset layers
		 */
		reset: function()
		{
			$('.dragPan').remove();
			
			if(map)
			{
				if(drawingManager)
				{
					map.removeInteraction(drawingManager)
				}
				
				if(highlight)
				{
					map.removeInteraction(highlight)
				}
				
				map.getLayers().forEach(function(layer, index)
				{
					if('draggable' == layer.get('initial')) return;
					
					layer.getSource().clear()
				})
			}
			
			if(popup)
			{
				popup.hide()
			}
			
			if(typeof geolocation !== 'undefined')
			{
				/* stop tracking to prevent map flicker */
				geolocation.setTracking(false)
			}
		},
		
		unzip: function(source, spinner)
		{
			if(!source) return false;
			
			if(localStorage.getItem(source.initial))
			{
				if(source.type == 'geojson')
				{
					openlayers.geojson(JSON.parse(localStorage.getItem(source.initial)), spinner)
				}
			}
			else if(typeof source.url !== 'undefined')
			{
				/* read kmz (zip) data */
				JSZipUtils.getBinaryContent(source.url, function(err, data)
				{
					if(err)
					{
						/* archive cannot be extracted, show error */
						console.log(err),
						
						$('.preloader').remove()
					}
					
					/* unzip archive */
					JSZip.loadAsync(data).then(function(zip)
					{
						/* read extracted datasource */
						Object.keys(zip.files).forEach(function(filename)
						{
							if('geojson' == filename.split('.').pop().toLowerCase())
							{
								/* create blob file from extracted data */
								zip.files[filename].async('string').then(function(blob)
								{
									/* write blob file */
									source.blob		= URL.createObjectURL(new Blob([blob], {type: 'geojson'}));
									
									localStorage.setItem(source.initial, JSON.stringify({initial: source.initial, blob: source.blob, url: source.url})),
									
									openlayers.geojson(source, spinner)
								})
							}
						})
					})
				})
			}
			else
			{
				/* unzip archive */
				JSZip.loadAsync(source).then(function(zip)
				{
					/* read extracted datasource */
					Object.keys(zip.files).forEach(function(filename)
					{
						if('geojson' == filename.split('.').pop().toLowerCase())
						{
							/* create blob file from extracted data */
							zip.files[filename].async('string').then(function(blob)
							{
								/* write blob file */
								source.blob			= URL.createObjectURL(new Blob([blob], {type: 'geojson'}));
								
								localStorage.setItem(source.initial, JSON.stringify({initial: source.initial, blob: source.blob, url: source.url})),
								
								openlayers.geojson(source, spinner)
							})
						}
					})
				})
			}
		},
		
		/**
		 * initialize polygon
		 */
		geojsonString: function(shapes, type)
		{
			var geojson								= new ol.format.GeoJSON(),
				sourceVector						= new ol.source.Vector
				({
				});
			
			if(typeof shapes.features !== 'undefined')
			{
				var sourceVector					= new ol.source.Vector
				({
					features: geojson.readFeatures
					(
						shapes,
						{
							featureProjection: map.getView().getProjection()
						}
					)
				});
			}
			else if(typeof shapes.geometry !== 'undefined')
			{
				var sourceVector					= new ol.source.Vector
				({
					features: geojson.readFeatures
					(
						{
							type: 'FeatureCollection',
							features: [shapes]
						},
						{
							featureProjection: map.getView().getProjection()
						}
					)
				});
			}
			
			layerVector								= new ol.layer.Vector
			({
				source: sourceVector,
				style: function(feature, resolution)
				{
					var pattern						= null;
					
					if(icon_pattern || feature.get('icon'))
					{
						var canvas					= document.createElement('canvas');
						var context					= canvas.getContext('2d');
						var image					= new Image();
						
						image.src					= (icon_pattern ? icon_pattern : feature.get('icon'));
						pattern						= context.createPattern(image, 'repeat');
					}
					
					return [new ol.style.Style
					({
						image: new ol.style.Icon
						({
							scale: (feature.get('icon-scale') ? feature.get('icon-scale') : 1),
							src: (icon_pattern ? icon_pattern : (feature.get('icon') ? feature.get('icon') : config.asset_url + 'openlayers/resources/icons/marker.png'))
						}),
						stroke: new ol.style.Stroke
						({
							color: (stroke_color ? stroke_color : (feature.get('stroke') ? feature.get('stroke') : hex_to_rgba('#ffffff', 0))),
							width: (feature.get('stroke-width') ? feature.get('stroke-width') : 3)
						}),
						fill: new ol.style.Fill
						({
							color: (pattern ? pattern : hex_to_rgba((fill_color ? fill_color : feature.get('fill')), (feature.get('fill-opacity') ? feature.get('fill-opacity') : .35)))
						}),
						text: new ol.style.Text
						({
							text: feature.get('title'),
							font: '14px Arial, sans-serif',
							fill: new ol.style.Fill
							({
								color: '#000000'
							}),
							stroke: new ol.style.Stroke
							({
								color: '#ffffff',
								width: 3
							})
						})
					})];
				}
			});
			
			/* push layer to map */
			map.addLayer(layerVector),
			
			/* fit map to features */
			(layerVector.getSource().getFeatures().length ? map.getView().fit(layerVector.getSource().getExtent(), {size: map.getSize()}) : null)
			
			if(type)
			{
				drawingManager						= new ol.interaction.Draw
				({
					type: (type == 'polygon' ? 'Polygon' : (type == 'linestring' ? 'LineString' : 'Point')),
					source: layerVector.getSource()
				});
				drawingType							= type;
				
				/* event on drawing end */
				drawingManager.on('drawend', function(event)
				{
					var drawn						= event.feature,
						features					= layerVector.getSource().getFeatures(),
						output						= features.concat(drawn),
						measure						= getMeasure(output);
					
					$(apply_coordinate).val(geojson.writeFeatures(output, {featureProjection: map.getView().getProjection()})),
					
					$(apply_measure_area).val(measure.area),
					
					$(apply_measure_distance).val(measure.distance)
				});
				
				var drag							= new ol.interaction.Translate
				({
					layers: [layerVector]
				});
				
				var modify							= new ol.interaction.Modify
				({
					source: layerVector.getSource()
				});
				
				var snap							= new ol.interaction.Snap
				({
					source: layerVector.getSource()
				});
				
				var highlight						= new ol.interaction.Select
				({
					layers: [layerVector]
				});
				
				/* add drawing manager */
				map.addInteraction(drawingManager),
				
				/* add feature drag interaction */
				map.addInteraction(drag),
				
				/* add feature modify interaction */
				map.addInteraction(modify),
				
				/* add feature snap interaction */
				map.addInteraction(snap),
				
				/* add feature snap interaction */
				map.addInteraction(highlight),
				
				/* on drag end */
				drag.on('translatestart', function(event)
				{
					popup.hide(),
					
					map.removeOverlay(popup)
				}),
				
				/* on drag end */
				drag.on('translateend', function(event)
				{
					modify.dispatchEvent
					({
						type: 'modifyend'
					})
				}),
				
				/* on modify end */
				modify.on('modifyend', function(event)
				{
					if(typeof event.target !== 'undefined' && typeof event.target.features_ !== 'undefined')
					{
						event.features				= event.target.features_;
					}
					
					var measure						= getMeasure(event.features.getArray()),
						coordinate					= 
						{
							lat: 0,
							lng: 0
						};
					
					event.features.getArray().forEach(function(layer, index)
					{
						coordinate					= ol.proj.transform(layer.getGeometry().flatCoordinates, map.getView().getProjection(), projection);
						coordinate					=
						{
							lat: coordinate[1],
							lng: coordinate[0]
						};
					}),
					
					$(apply_coordinate).val(geojson.writeFeatures(event.features.getArray(), {featureProjection: map.getView().getProjection()})),
					
					$(apply_measure_area).val(measure.area),
					
					$(apply_measure_distance).val(measure.distance);
					
					if('google' == config.openlayers_search_provider)
					{
						var finder					= new google.maps.Geocoder();
						
						finder.geocode({location: coordinate}, function(results, status)
						{
							if(status === 'OK')
							{
								$(apply_address).val(results[0].formatted_address).trigger('change'),
								$('#gcd-input-query').val(results[0].formatted_address)
							}
							else
							{
								console.log(status)
							}
						})
					}
					else
					{
						$.get('https://nominatim.openstreetmap.org/reverse?accept-language=id&format=json&lat=' + coordinate.lat + '&lon=' + coordinate.lng + '&addressdetails=1', function(response)
						{
							$(apply_address).val(response.display_name).trigger('change'),
							
							$('#gcd-input-query').val(response.display_name)
						})
					}
				}),
				
				highlight.on('select', function(event)
				{
					if(event.selected.length)
					{
						map.addOverlay(popup),
						
						popup.show(event.mapBrowserEvent.coordinate, ('<div class="popup-placeholder"><div class="pt-2 pr-3 pb-2 pl-3 border-bottom"><label class="font-weight-bold d-block mb-0"><i class="mdi mdi-menu" style="width:30px; display:inline-block"></i> ' + (phrase.options ? phrase.options : 'Options') + '</label></div><div class="popup-content"><div class="list-group list-group-flush"><a href="javascript:void(0)" class="list-group-item list-group-item-action p-3" onclick="removeFeature()"><i class="mdi mdi-trash-can-outline"></i> ' + (phrase.remove_feature ? phrase.remove_feature : 'Remove feature') + '</a></div></div></div>'))
						
						highlighted					= event.selected[0];
					}
					else
					{
						popup.hide(),
						
						map.removeOverlay(popup)
					}
				})
			}
		},
		
		/**
		 * render from GeoJSON
		 */
		geojson: function(source, spinner)
		{
			$('.preloader').remove(),
			
			/* add spinner indicator */
			(spinner ? ($('<div class="absolute top right bottom left preloader"></div>').appendTo(_this), spinner.removeClass('mdi-magnify').addClass('mdi-loading mdi-spin')) : '');
			
			/* initialize layer */
			layerVector								= new ol.layer.Vector
			({
				source: new ol.source.Vector
				({
					url: (typeof source.blob !== 'undefined' ? source.blob : (typeof source.url !== 'undefined' ? source.url : source)),
					format: new ol.format.GeoJSON
					({
						extractStyles: true
					}),
					projection: map.getView().getProjection()
				}),
				style: function(feature, resolution)
				{
					var pattern						= null;
					
					if(feature.get('icon'))
					{
						var canvas					= document.createElement('canvas');
						var context					= canvas.getContext('2d');
						var image					= new Image();
						
						image.src					= feature.get('icon');
						pattern						= context.createPattern(image, 'repeat');
					}
					
					if(feature.get('selected'))
					{
						selected.push(feature)
					}
					
					return [new ol.style.Style
					({
						image: new ol.style.Icon
						({
							scale: (feature.get('icon-scale') ? feature.get('icon-scale') : 1),
							src: (feature.get('icon') ? feature.get('icon') : config.asset_url + 'openlayers/resources/icons/marker.png')
						}),
						stroke: new ol.style.Stroke
						({
							color: (feature.get('stroke') ? feature.get('stroke') : hex_to_rgba('#ffffff', 0)),
							width: (feature.get('stroke-width') ? feature.get('stroke-width') : 3)
						}),
						fill: new ol.style.Fill
						({
							color: (pattern ? pattern : hex_to_rgba(feature.get('fill'), (feature.get('fill-opacity') ? feature.get('fill-opacity') : .35)))
						}),
						text: new ol.style.Text
						({
							text: (feature.get('layer_type') && $.inArray(feature.get('layer_type'), ['polygon', 'linestring']) !== -1 ? feature.get('label') : null),
							font: '14px Arial, sans-serif',
							fill: new ol.style.Fill
							({
								color: '#000000'
							}),
							stroke: new ol.style.Stroke
							({
								color: '#ffffff',
								width: 3
							})
						})
					})];
				},
				initial: source.initial,
				url: source.url
			});
			
			/* push layer to map */
			map.addLayer(layerVector),
			
			/* fit map to features */
			layerVector.getSource().once('change',function(e)
			{
				if(layerVector.getSource().getState() === 'ready')
				{ 
					var bounds_selected				= ol.extent.createEmpty(),
						bounds_all					= ol.extent.createEmpty(),
						found_selected				= false,
						found_all					= false;
					
					for(var i = 0; i < layerVector.getSource().getFeatures().length; i++)
					{
						if(layerVector.getSource().getFeatures()[i].getGeometry())
						{
							if(layerVector.getSource().getFeatures()[i].get('selected'))
							{
								found_selected		= true;
								
								ol.extent.extend(bounds_selected, layerVector.getSource().getFeatures()[i].getGeometry().getExtent())
							}
							else
							{
								found_all			= true;
								
								ol.extent.extend(bounds_all, layerVector.getSource().getFeatures()[i].getGeometry().getExtent())
							}
						}
					}
					
					if(found_selected)
					{
						map.getView().fit(bounds_selected, {size: map.getSize()})
					}
					else if(found_all)
					{
						map.getView().fit(bounds_all, {size: map.getSize()})
					}
				}
			}),
			
			/* on map click event */
			map.on('singleclick', function(event)
			{
				var selected						= highlight.getFeatures();
				
				if(c) return false;
				
				var clicked							= map.forEachFeatureAtPixel(event.pixel, function(point, layer)
				{
					return {
						point: point,
						layer: layer
					};
				});
				
				if(clicked && typeof clicked.point !== 'undefined')
				{
					if(typeof clicked.point.get('object_id') === 'undefined' || !clicked.layer) return;
					
					selected.clear(),
					
					selected.push(clicked.point),
					
					popup.show(event.coordinate, ('<div class="popup-placeholder"><div class="pt-2 pr-3 pb-2 pl-3 border-bottom"><label class="font-weight-bold d-block mb-0">' + (clicked.point.get('title') ? clicked.point.get('title') : clicked.layer.get('title')) + '</label></div><div class="popup-content p-3"></div></div>')),
					console.log(clicked.layer.get('url')),
					$.ajax
					({
						url: clicked.layer.get('url'),
						method: 'POST',
						data:
						{
							targetFeature: clicked.point.get('object_id')
						},
						beforeSend: function()
						{
							c						= true;
							
							$('.popup-content').html('<div class="d-flex justify-content-center"><div class="spinner-border" role="status"><span class="sr-only">' + (phrase.loading ? phrase.loading : 'Loading...') + '</span></div></div>')
						}
					})
					.done(function(response)
					{
						c							= false;
						
						$('.popup-content, .identification-information').html((typeof response.content!== 'undefined' ? response.content : ''))
					})
				}
				else
				{
					selected.clear(),
					popup.hide()
				}
			});
			
			/* add event listener to remove spinner */
			var listener							= layerVector.getSource().once('change', function(e)
			{
				if(layerVector.getSource().getState() == 'ready')
				{
					/* remove spinner */
					(spinner ? spinner.removeClass('mdi-loading mdi-spin').addClass('mdi-magnify') : ''),
					$('.preloader').remove(),
					
					/* unbind listener */
					ol.Observable.unByKey(listener)
				}
			});
		},
		
		arcgis: function(rest_url)
		{
			layerVector								= new ol.layer.Tile
			({
				source: new ol.source.TileArcGISRest
				({
					url: rest_url
				})
			});
			
			/* push layer to map */
			map.addLayer(layerVector),
			
			/* on map click event */
			map.on('singleclick', function(event)
			{
				if(c) return false;
				
				var extent							= ol.extent.boundingExtent
				([ 
					map.getCoordinateFromPixel([event.pixel[0] - 1, event.pixel[1] - 1]),
					map.getCoordinateFromPixel([event.pixel[0] + 1, event.pixel[1] - 1]),
					map.getCoordinateFromPixel([event.pixel[0] + 1, event.pixel[1] + 1]),
					map.getCoordinateFromPixel([event.pixel[0] - 1, event.pixel[1] + 1])
				]);
				
				extent								= ol.proj.transformExtent([extent[0], extent[1], extent[2], extent[3]], map.getView().getProjection(), projection);
				
				popup.show(event.coordinate, ('<div class="popup-placeholder"><div class="pt-2 pr-3 pb-2 pl-3 border-bottom"><label class="font-weight-bold d-block mb-0">...</label></div><div class="popup-content p-3"></div></div>')),
				
				$.ajax
				({
					url: rest_url + '/query/?f=json',
					data:
					{
						xmin: extent[0],
						ymin: extent[1],
						xmax: extent[2],
						ymax: extent[3]
					},
					dataType: 'jsonp',
					beforeSend: function()
					{
						c							= true;
						$('.popup-content').html('<div class="d-flex justify-content-center"><div class="spinner-border" role="status"><span class="sr-only">' + (phrase.loading ? phrase.loading : 'Loading...') + '</span></div></div>')
					}
				})
				.done(function(response)
				{
					c								= false;
					
					$('.popup-content, .identification-information').html((typeof response.serviceDescription!== 'undefined' ? response.serviceDescription : ''))
				})
			})
		},
		
		/**
		 * create draggable marker
		 */
		coordinate: function(draggable)
		{
			draggableMarker							= new ol.layer.Vector
			({
				source: new ol.source.Vector
				({
					features:
					[
						new ol.Feature
						({
							type: 'click',
							title: '<label class="d-block font-weight-bold text-muted mb-0">' + (phrase.default_marker ? phrase.default_marker : 'Default Marker') + '</label>',
							description: '<p class="mb-0">' + (phrase.this_can_be_drag_on_edit_mode ? phrase.this_can_be_drag_on_edit_mode : 'This can be drag on edit mode') + '</p>',
							geometry: new ol.geom.Point(map.getView().getCenter())
						})
					]
				}),
				style: new ol.style.Style
				({
					image: new ol.style.Icon
					({
						anchor: [.5, 1],
						scale: .4,
						src: config.asset_url + 'openlayers/resources/icons/marker.png'
					})
				}),
				initial: 'draggable'
			});
			
			/* push layer to map */
			map.addLayer(draggableMarker);
			
			if(draggable)
			{
				var drag							= new ol.interaction.Translate
				({
					features: new ol.Collection(draggableMarker.getSource().getFeatures())
				});
				
				/* add drag interaction to marker */
				map.addInteraction(drag),
					
				/* event on marker drag end */
				drag.on('translateend', function(event)
				{
					/* dragend event */
					if(typeof geolocation !== 'undefined')
					{
						geolocation.setTracking(false)
					}
					
					var coordinate					= ol.proj.transform(event.coordinate, map.getView().getProjection(), projection);
					console.log(coordinate);
					var coordinate					=
					{
						lat: coordinate[1],
						lng: coordinate[0]
					};
					
					if('google' == config.openlayers_search_provider)
					{
						var finder					= new google.maps.Geocoder();
						
						finder.geocode({location: coordinate}, function(results, status)
						{
							if(status === 'OK')
							{
								$(apply_address).val(results[0].formatted_address).trigger('change'),
								$('#gcd-input-query').val(results[0].formatted_address)
							}
							else
							{
								console.log(status)
							}
						})
					}
					else
					{
						/* getting the address name */
						$.get('https://nominatim.openstreetmap.org/reverse?accept-language=id&format=json&lat=' + coordinate.lat + '&lon=' + coordinate.lng + '&addressdetails=1', function(response)
						{
							/* apply to inputs */
							$(apply_address).val(response.display_name).trigger('change'),
							
							$('#gcd-input-query').val(response.display_name)
						})
					}
					
					$(apply_coordinate).val(JSON.stringify(coordinate))
				})
			}
		},
		
		/**
		 * add geocoder (place search)
		 */
		geocoder: function(_this)
		{
			geocoder								= new Geocoder
			(
				'nominatim',
				{
					provider: ('openlayers' == config.openlayers_search_provider ? 'osm' : config.openlayers_search_provider), /* available provider: osm, mapquest (require key), photon, pelias(require key), bing(require key) and opencage(require key) */
					key: config.openlayers_search_key, /* api key */
					targetType: 'text-input',
					countrycodes: 'id',
					placeholder: (phrase.search_place ? phrase.search_place : 'Search Place'),
					limit: 10,
					autoComplete: true,
					featureStyle: null
				}
			);
			
			/* add geocoder into map controls directories */
			map.addControl(geocoder);
			
			if('google' == config.openlayers_search_provider)
			{
				var autocomplete					= new google.maps.places.Autocomplete
				(
					(document.getElementById('gcd-input-query')),
					{
						componentRestrictions:
						{
							country: 'id'
						},
						fields: ['formatted_address', 'geometry', 'name'],
						strictBounds: false,
						types: ['establishment']
					}
				);
				
				google.maps.event.addListener(autocomplete, 'place_changed', function()
				{
					var place						= autocomplete.getPlace();
					
					if(typeof place.geometry === 'undefined')
					{
						return;
					}
					
					if(typeof geolocation !== 'undefined')
					{
						geolocation.setTracking(false)
					}
					
					if(!drawingType)
					{
						/* apply to inputs */
						$(apply_coordinate).val(JSON.stringify({lat: place.geometry.location.lat(), lng: place.geometry.location.lng()}))
					}
					
					$(_this.attr('data-apply-address-to')).val(place.formatted_address).trigger('change'),
					
					map.getView().setCenter(ol.proj.transform([place.geometry.location.lng(), place.geometry.location.lat()], projection, map.getView().getProjection()));
					
					if(draggableMarker)
					{
						draggableMarker.getSource().getFeatures()[0].setGeometry(new ol.geom.Point(ol.proj.transform([place.geometry.location.lng(), place.geometry.location.lat()], projection, map.getView().getProjection()))),
					
						map.getView().fit(draggableMarker.getSource().getExtent(), map.getSize())
					}
					
					if(_this.attr('data-finder-url'))
					{
						$.ajax
						({
							url: _this.attr('data-finder-url'),
							method: 'POST',
							data:
							{
								lat: place.geometry.location.lat(),
								lng: place.geometry.location.lng()
							},
							beforeSend: function()
							{
								openlayers.reset(),
								$('.preloader').remove(),
								$('[role=map]').append('<div class="absolute top right bottom left preloader"></div>')
							}
						})
						.done(function(response)
						{
							if(typeof response.url !== 'undefined')
							{
								openlayers.unzip(response)
							}
						})
					}
				})
			}
			else
			{
				/* create event when address is chosen */
				geocoder.on('addresschosen', function(response)
				{
					if(typeof geolocation !== 'undefined')
					{
						geolocation.setTracking(false)
					}
					
					var coordinate					= ol.proj.transform(response.coordinate, map.getView().getProjection(), projection);
					var coordinate					=
					{
						lat: coordinate[1],
						lng: coordinate[0]
					};
					
					if(!drawingType)
					{
						/* apply to inputs */
						$(apply_coordinate).val(JSON.stringify(coordinate))
					}
					
					$(_this.attr('data-apply-address-to')).val(response.address.details.name).trigger('change'),
					
					$('#gcd-input-query').val(response.address.details.name),
					
					map.getView().setCenter(response.coordinate);
					
					if(draggableMarker)
					{
						draggableMarker.getSource().getFeatures()[0].setGeometry(new ol.geom.Point(response.coordinate)),
					
						map.getView().fit(draggableMarker.getSource().getExtent(), map.getSize())
					}
					
					if(_this.attr('data-finder-url'))
					{
						$.ajax
						({
							url: _this.attr('data-finder-url'),
							method: 'POST',
							data:
							{
								lat: coordinate.lat,
								lng: coordinate.lng
							},
							beforeSend: function()
							{
								openlayers.reset(),
								$('.preloader').remove(),
								$('[role=map]').append('<div class="absolute top right bottom left preloader"></div>')
							}
						})
						.done(function(response)
						{
							if(typeof response.url !== 'undefined')
							{
								openlayers.unzip(response)
							}
						})
					}
				})
			}
		},
		
		/**
		 * add geolocation (user based location)
		 */
		geolocation: function()
		{
			if(navigator.geolocation)
			{
				geolocation							= new ol.Geolocation
				({
					projection: map.getView().getProjection(),
					tracking: true,
					trackingOptions:
					{
						enableHighAccuracy: true,
						maximumAge: 2000
					}
				});
				
				/* on device position change */
				geolocation.on('change', function()
				{
					map.getView().setCenter(geolocation.getPosition());
					
					if(draggableMarker)
					{
						draggableMarker.getSource().getFeatures()[0].setGeometry(new ol.geom.Point(geolocation.getPosition())),
					
						map.getView().fit(draggableMarker.getSource().getExtent(), map.getSize());
						
						if(!drawingType)
						{
							var coordinate			= ol.proj.transform(geolocation.getPosition(), map.getView().getProjection(), projection);
							var coordinate			=
							{
								lat: coordinate[1],
								lng: coordinate[0]
							};
							
							/* apply to inputs */
							$(apply_coordinate).val(JSON.stringify(coordinate))
						}
					}
					
				});
				
				var handleTrack						= function(e)
				{
					map.getView().setCenter(geolocation.getPosition());
					
					if(draggableMarker)
					{
						draggableMarker.getSource().getFeatures()[0].setGeometry(new ol.geom.Point(geolocation.getPosition())),
					
						map.getView().fit(draggableMarker.getSource().getExtent(), map.getSize());
						
						if(!drawingType)
						{
							var coordinate			= ol.proj.transform(geolocation.getPosition(), map.getView().getProjection(), projection);
							var coordinate			=
							{
								lat: coordinate[1],
								lng: coordinate[0]
							};
							
							/* apply to inputs */
							$(apply_coordinate).val(JSON.stringify(coordinate))
						}
					}
				};
				
				var icon							= document.createElement('i');
				var button							= document.createElement('button');
				var element							= document.createElement('div');
				
				icon.setAttribute('class', 'mdi mdi-crosshairs-gps'),
				button.setAttribute('title', (phrase.track_me ? phrase.track_me : 'Track Me')),
				button.setAttribute('type', 'button'),
				button.appendChild(icon),
				button.addEventListener('click', handleTrack, false),
				element.setAttribute('class', 'ol-track ol-unselectable ol-control'),
				element.appendChild(button),
				
				map.addControl
				(
					new ol.control.Control
					({
						element: element
					})
				)
			};
		},
		
		/**
		 * routing machine
		 */
		routing: function(source, destination)
		{
			$.get('//router.project-osrm.org/route/v1/driving/' + source.join() + ';' + destination.join(), function(response)
			{
				if(typeof response.code === 'undefined' || response.code !== 'Ok')
				{
					console.log('No route found');
					
					return;
				}
				
				$.each(response.waypoints, function(key, val)
				{
					/* push source point to map */
					map.addLayer
					(
						new ol.layer.Vector
						({
							source: new ol.source.Vector
							({
								features:
								[
									new ol.Feature
									({
										type: 'place',
										geometry: new ol.geom.Point(ol.proj.fromLonLat(val.location))
									})
								]
							}),
							style: new ol.style.Style
							({
								image: new ol.style.Icon
								({
									anchor: [.5, 1],
									scale: .5,
									src: config.asset_url + 'openlayers/resources/icons/marker.png'
								})
							})
						})
					)
				}),
				
				$.each(response.routes, function(key, val)
				{
					/* push route to map */
					map.addLayer
					(
						new ol.layer.Vector
						({
							source: new ol.source.Vector
							({
								features:
								[
									new ol.Feature
									({
										type: 'route',
										geometry: new ol.format.Polyline
										({
											factor: 1e5
										})
										.readGeometry
										(
											val.geometry,
											{
												dataProjection: 'EPSG:4326',
												featureProjection: 'EPSG:3857'
											}
										)
									})
								]
							}),
							style: new ol.style.Style
							({
								stroke: new ol.style.Stroke
								({
									width: 5,
									color: [40, 40, 40, 0.8]
								})
							})
						})
					)
				})
			})
		},
		
		/**
		 * pulsate effect
		 */
		pulsate: function pulsate(feature, style, duration)
		{
			var start								= new Date().getTime(),
				key									= map.on('postcompose', function(event)
			{
				var vectorContext					= event.vectorContext,
					frameState						= event.frameState,
					flashGeom						= feature.getGeometry().clone(),
					elapsed							= frameState.time - start,
					elapsedRatio					= elapsed / duration,
					radius							= ol.easing.easeOut(elapsedRatio) * 35 + 6,
					opacity							= ol.easing.easeOut(1 - elapsedRatio),
					fillOpacity						= ol.easing.easeOut(0.3 - elapsedRatio);
					
				vectorContext.setStyle(new ol.style.Style
				({
					image: new ol.style.Circle
					({
						radius: radius,
						fill: new ol.style.Fill
						({
							color: 'rgba(255, 0, 0, ' + (fillOpacity) + ')'
						}),
						stroke: new ol.style.Stroke
						({
							color: 'rgba(255, 0, 0, ' + fillOpacity + ')',
							width: 1 + opacity
						})
					})
				})),
				
				vectorContext.drawGeometry(flashGeom),
				
				vectorContext.setStyle(style),
				
				vectorContext.drawGeometry(feature.getGeometry());
				
				if(elapsed > duration)
				{
					ol.Observable.unByKey(key),
					openlayers.pulsate(feature, style, duration)
				}
				
				map.render()
			})
		},
		
		/**
		 * selection
		 */
		selection: function(appendTo)
		{
			selected								= highlight.getFeatures();
			selectionBox							= new ol.interaction.DragBox
			({
				condition: ol.events.condition.platformModifierKeyOnly
			});
			
			map.addInteraction(selectionBox),
			
			selectionBox.on('boxend', function(e)
			{
				var extent							= selectionBox.getGeometry().getExtent(),
					resolution						= map.getView().getResolution(),
					exists							= [];
				
				map.getLayers().getArray().forEach(function(layer, index)
				{
					if(!index) return;
					
					layer.getSource().forEachFeatureIntersectingExtent(extent, function(feature)
					{
						var group					= feature.get('group_id');
						
						if(typeof exists[group] !== 'undefined')
						{
							exists[group]++;
						}
						else
						{
							exists[group]			= 1;
						}
						
						selected.push(feature);
					})
				}),
				
				map.getView().fit(extent, {size: map.getSize()}),
				
				map.getView().setResolution(resolution),
				
				$.each(exists, function(key, val)
				{
					if(val)
					{
						$(appendTo).find('[data-initial=' + key + ']').find('.badge').addClass('badge-info').text(val.toLocaleString('en'));
						$(appendTo).find('[data-initial=' + key + ']').find('input[type=checkbox]').prop('checked', true),
						$(appendTo).find('[data-initial=' + key + ']').find('label').removeClass('text-muted')
					}
				})
			}),
			
			selectionBox.on('boxstart', function(e)
			{
				selected.clear(),
				$(appendTo).find('.badge').text(''),
				$(appendTo).find('input[type=checkbox]').prop('checked', false),
				$(appendTo).find('label').addClass('text-muted')
			}),
			
			map.on('click', function()
			{
				selected.clear(),
				$(appendTo).find('.badge').text(''),
				$(appendTo).find('input[type=checkbox]').prop('checked', false),
				$(appendTo).find('label').addClass('text-muted')
			})
		},
		
		/**
		 * identification
		 */
		identification: function(appendTo)
		{
			selected								= highlight.getFeatures();
			selectionBox							= new ol.interaction.DragBox
			({
				condition: ol.events.condition.platformModifierKeyOnly
			});
			
			map.addInteraction(selectionBox),
			
			selectionBox.on('boxend', function(e)
			{
				var info							= [],
					extent							= selectionBox.getGeometry().getExtent(),
					resolution						= map.getView().getResolution(),
					exist							= [],
					num								= 0;
				
				$(appendTo).html(''),
				
				map.getLayers().getArray().forEach(function(layer, index)
				{
					if(!index) return;
					
					layer.getSource().forEachFeatureIntersectingExtent(extent, function(feature)
					{
						if(typeof feature.get('object_id') !== 'undefined')
						{
							var object_id			= feature.get('object_id');
						}
						else
						{
							/* get primary id from description that contain object_id attribute */
							var object_id			= feature.get('description').toLowerCase() + ' ',
								object_id			= object_id.replace(/(<b[^>]+?>|<b>|<\/b>)/ig, ' '),
								object_id			= object_id.replace(/\s\s+/g, ' '),
								object_id			= object_id.substring(object_id.lastIndexOf('object_id = ') + 11),
								object_id			= object_id.substr(0, object_id.indexOf(' '));
						}
						
						feature.setId(object_id),
						
						selected.push(feature);
					
						if($.inArray(object_id, exist) === -1)
						{
							exist.push(object_id),
							
							$('<div class="identification-item pt-2 pb-2' + ($('.identification-information').children().length > 0 ? ' border-top' : '') + '" data-title="' + feature.get('title') + '" data-url="' + layer.get('url') + '" data-target="' + object_id + '" style="cursor:pointer">' + (feature.get('label') ? feature.get('label') : feature.get('title')) + '</div>').appendTo(appendTo)
						}
					}),
					
					$('body').off('mouseover.identification-item'),
					$('body').on('mouseover.identification-item', '.identification-item', function(e)
					{
						if(layer.getSource().getFeatureById($(this).attr('data-target')))
						{
							popup.show(layer.getSource().getFeatureById($(this).attr('data-target')).getGeometry().getExtent(), ('<div class="popup-placeholder"><div class="pt-2 pr-3 pb-2 pl-3 border-bottom"><label class="font-weight-bold d-block mb-0 popup-title">' + $(this).attr('data-title') + '</label></div><div class="popup-content p-3"><label class="d-block text-muted text-center pt-3 pb-3">' + (phrase.click_to_get_detail ? phrase.click_to_get_detail : 'Click to get detail') + '</label></div></div>'))
						}
					}),
					
					$('body').off('mouseout.identification-item'),
					$('body').on('mouseout.identification-item', '.identification-item', function(e)
					{
						popup.hide()
					}),
					
					$('body').off('click.identification-item touch.identification-item'),
					$('body').on('click.identification-item touch.identification-item', '.identification-item', function(e)
					{
						if(layer.getSource().getFeatureById($(this).attr('data-target')))
						{
							map.getView().fit(layer.getSource().getFeatureById($(this).attr('data-target')).getGeometry().getExtent(), {size: map.getSize(), zoom: maxZoom}),
							
							$.ajax
							({
								url: $(this).attr('data-url'),
								method: 'POST',
								data:
								{
									targetFeature: $(this).attr('data-target'),
									coordinate: ol.proj.transform(event.coordinate, map.getView().getProjection(), projection)
								},
								beforeSend: function()
								{
									$('.popup-content').html('<div class="d-flex justify-content-center"><div class="spinner-border" role="status"><span class="sr-only">' + (phrase.loading ? phrase.loading : 'Loading...') + '</span></div></div>')
								}
							})
							.done(function(response)
							{
								$('.popup-title').html(response.title),
								$('.popup-content').html(response.content)
							})
						}
					})
				}),
				
				map.getView().fit(extent, {size: map.getSize()}),
				
				map.getView().setResolution(resolution)
			}),
			
			selectionBox.on('boxstart', function(e)
			{
				selected.clear()
			}),
			
			map.on('click', function()
			{
				$(appendTo).html(''),
				selected.clear()
			})
		},
		
		/**
		 * measurement
		 */
		measurement: function(type, appendTo)
		{
			$(appendTo).html('');
			
			if(typeof map !== 'undefined' && typeof drawingManager !== 'undefined')
			{
				map.removeInteraction(drawingManager);
				drawingManager						= null;
			}
			
			if(measurementVector)
			{
				measurementVector.getSource().clear()
			}
			
			if(type == 'area' || type == 'distance')
			{
				measurementVector					= new ol.layer.Vector
				({
					source: new ol.source.Vector(),
					style: new ol.style.Style
					({
						stroke: new ol.style.Stroke
						({
							color: colorscheme,
							width: 3
						}),
						fill: new ol.style.Fill
						({
							color: hex_to_rgba(colorscheme, .15)
						})
					})
				});
				
				drawingManager						= new ol.interaction.Draw
				({
					type: ('area' == type ? 'Polygon' : 'LineString'),
					source: measurementVector.getSource()
				});
				
				/* push layer to map */
				map.addLayer(measurementVector),
				
				/* create drawing tools */
				map.addInteraction(drawingManager),
				
				/* event on drawing end */
				drawingManager.on('drawstart', function(event)
				{
					measurementVector.getSource().clear()
				}),
				
				/* event on drawing end */
				drawingManager.on('drawend', function(event)
				{
					var key							= (features.length ? parseInt(features.length + 1) : 0),
						prepare						= [];
					features[key]					= event.feature.getGeometry().getCoordinates();
					
					$.each(features, function(_key, _val)
					{
						if(!_val) return;
						
						var coord					= [],
							_val					= ('area' == type ? _val[0] : _val);
						$.each(_val, function(__key, __val)
						{
							coord.push(ol.proj.transform(__val, map.getView().getProjection(), projection))
						}),
						prepare.push
						({
							type: 'Feature',
							geometry:
							{
								type: ('area' == type ? 'Polygon' : 'LineString'),
								coordinates: ('area' == type ? [coord] : coord)
							},
							properties:
							{
							}
						})
					});
					
					var meter						= (Math.round('area' == type ? event.feature.getGeometry().getArea() : event.feature.getGeometry().getLength())),
						value						= $(appendTo).prev('.form-group').find('select').val(),
						label						= $(appendTo).prev('.form-group').find('select option:selected').html(),
						result						= ('area' == type ? ('ac' == value ? meter / 4047 : ('mi' == value ? meter / 2.59e+6 : ('ha' == value ? meter / 10000 : ('yd' == value ? meter / 1.196 : ('ft' == value ? meter / 10.764 : ('km' == value ? meter / 1000 : meter)))))) : ('mi' == value ? meter / 1609 : ('yd' == value ? meter / 1.094 : ('ft' == value ? meter / 10.764 : ('km' == value ? meter / 1000 : meter)))));
						
					$(appendTo).html
					(
						'<div class="text-center mt-3">' +
							'<h6>' +
								(phrase.measurement_result ? phrase.measurement_result : 'Measurement result') +
							'</h6>' +
							'<h3>' +
								result.toLocaleString('en') +
							'</h3>' +
							'<h3>' +
								label +
							'</h3>' +
						'</div>'
					)
				})
			}
			else
			{
				map.on('singleclick', function(event)
				{
					var coordinate					= ol.proj.transform(event.coordinate, map.getView().getProjection(), projection),
						hdms						= ol.coordinate.toStringHDMS(coordinate, 5);
					
					if(measurementVector)
					{
						measurementVector.getSource().clear()
					}
					
					measurementVector				= new ol.layer.Vector
					({
						source: new ol.source.Vector
						({
							features:
							[
								new ol.Feature
								({
									geometry: new ol.geom.Point(ol.proj.fromLonLat([coordinate[0], coordinate[1]]))
								})
							]
						}),
						style: new ol.style.Style
						({
							image: new ol.style.Icon
							({
								anchor: [.5, 1],
								scale: .5,
								src: config.asset_url + 'openlayers/resources/icons/marker.png'
							})
						})
					});
					
					/* push layer to map */
					map.addLayer(measurementVector)
					
					$(appendTo).html
					(
						'<div class="text-center mt-3">' +
							'<h6 class="mb-3">' +
								(phrase.measurement_result ? phrase.measurement_result : 'Measurement result') +
							'</h6>' +
							'<div class="row">' +
								'<div class="col-6 font-weight-bold">' +
									(phrase.latitude ? phrase.latitude : 'Latitude') +
								'</div>' +
								'<div class="col-6 font-weight-bold">' +
									(phrase.longitude ? phrase.longitude : 'Longitude') +
								'</div>' +
							'</div>' +
							'<div class="row form-group">' +
								'<div class="col-6">' +
									coordinate[1].toString().substring(0, 12) +
								'</div>' +
								'<div class="col-6">' +
									coordinate[0].toString().substring(0, 12) +
								'</div>' +
							'</div>' +
							'<div class="form-group">' +
								'<label class="d-block font-weight-bold">HDMS</label>' +
								'<p>' +
									hdms +
								'</p>' +
							'</div>' +
						'</div>'
					)
				})
			}
		},
		
		/**
		 * upload
		 */
		upload: function(data, title)
		{
			if('kmz' !== data.name.split('.').pop().toLowerCase())
			{
				alert('Only KMZ file are allowed!');
				return;
			}
			
			if(map && layerOverlap)
			{
				map.removeLayer(layerOverlap)
			}
			
			/* unzip archive */
			JSZip.loadAsync(data).then(function(zip)
			{
				/* read extracted datasource */
				Object.keys(zip.files).forEach(function(filename)
				{
					/* check if the extracted data is a valid KML format */
					if('kml' !== filename.split('.').pop().toLowerCase()) return;
					
					/* create blob file from extracted data */
					zip.files[filename].async('string').then(function(blob)
					{
						/* write blob file */
						var blob					= new Blob([blob], {type: 'kml'}),
							blobURL					= URL.createObjectURL(blob);
						
						/* initialize layer */
						layerOverlap				= new ol.layer.Vector
						({
							source: new ol.source.Vector
							({
								projection: ol.proj.get(projection),
								url: blobURL,
								format: new ol.format.KML
								({
									extractStyles: true
								})
							}),
							type: 'xml',
							title: title
						});
						
						/* push layer to map */
						map.addLayer(layerOverlap),
						
						/* fit map to features */
						layerOverlap.getSource().once('change',function(e)
						{
							if(layerOverlap.getSource().getState() === 'ready')
							{ 
								var bounds			= ol.extent.createEmpty();
								
								for(var i = 0; i < layerOverlap.getSource().getFeatures().length; i++)
								{
									ol.extent.extend(bounds, layerOverlap.getSource().getFeatures()[i].getGeometry().getExtent())
								}
								console.log(bounds);
								if(typeof bounds[0] !== 'undefined' && bounds[0] !== Infinity)
								{
									map.getView().fit(bounds, {size: map.getSize()})
								}
							}
						})
					})
				})
			})
		},
		
		/**
		 * download
		 */
		download: function(type, size, resolution)
		{
			if('png' == type)
			{
				//map.once('rendercomplete', function()
				//{
					var canvas						= $('.ol-layer canvas');
					
					if(canvas && typeof canvas[0] !== 'undefined')
					{
						if(navigator.msSaveBlob)
						{
							navigator.msSaveBlob(canvas[0].msToBlob(), 'map.png')
						}
						else
						{
							var link				= document.getElementById('image-download');
							link.href				= canvas[0].toDataURL();
							
							link.click()
						}
					}
				//})
			}
			else if('pdf' == type)
			{
				//map.once('rendercomplete', function()
				//{
					var canvas						= $('.ol-layer canvas');
					
					if(canvas && typeof canvas[0] !== 'undefined')
					{
						require.js([config.asset_url + 'jspdf/jspdf.min.js'], function()
						{
							var resolution			= ($.inArray(resolution, ['a0', 'a1', 'a2', 'a3', 'a4', 'a5']) !== -1 ? resolution : 'a4'),
								doc					= new jsPDF('l', 'mm', resolution);
							
							doc.addImage(canvas[0].toDataURL(), 'PNG', 0, 0, doc.internal.pageSize.getWidth(), doc.internal.pageSize.getHeight())
							doc.save('map.pdf')
						})
					}
				//})
			}
		}
	}
})();

function getMeasure(features)
{
	var area										= 0,
		distance									= 0;
	
	features.forEach(function(feature)
	{
		var geometry								= feature.getGeometry();
			
		if(geometry instanceof ol.geom.Polygon)
		{
			area									+= (Math.round(geometry.getArea() * 100) / 100);
			distance								+= (Math.round((new ol.geom.LineString(geometry.getLinearRing(0).getCoordinates())).getLength() * 100) / 100);
		}
		else if(geometry instanceof ol.geom.LineString)
		{
			distance								+= (Math.round(geometry.getLength() * 100) / 100);
		}
	});
	
	return {
		area: area.toFixed(2),
		distance: distance.toFixed(2)
	};
}

function removeFeature()
{
	layerVector.getSource().removeFeature(highlighted);
	
	var geojson										= new ol.format.GeoJSON(),
		measure										= getMeasure(layerVector.getSource().getFeatures());
	
	$(apply_coordinate).val(geojson.writeFeatures(layerVector.getSource().getFeatures(), {featureProjection: map.getView().getProjection()})),
	
	$(apply_measure_area).val(measure.area),
	
	$(apply_measure_distance).val(measure.distance),
	
	popup.hide(),
	
	map.removeOverlay(popup)
}
For more information send a message to info at phpclasses dot org.