From 5be662f756540fff10a35cb5b1caa933a257e62e Mon Sep 17 00:00:00 2001
From: Jacek Kowalski <Jacek@jacekk.info>
Date: Sat, 06 Jul 2019 00:01:22 +0000
Subject: [PATCH] Add search feature to the map

---
 map.js |  211 ++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 155 insertions(+), 56 deletions(-)

diff --git a/map.js b/map.js
index c6f8bdc..bd9765b 100644
--- a/map.js
+++ b/map.js
@@ -1,4 +1,4 @@
-"use strict";
+'use strict';
 
 var ttss_refresh = 10000; // 10 seconds
 var ttss_position_type = 'RAW';
@@ -20,7 +20,7 @@
 var vehicles_info = {};
 
 var stops_xhr = null;
-var stops_ignored = ['131', '744'];
+var stops_ignored = ['131', '744', '1263', '3039'];
 var stops_style = {
 	'sb': new ol.style.Style({
 		image: new ol.style.Circle({
@@ -70,6 +70,7 @@
 var map = null;
 
 var panel = null;
+var find = null;
 
 var fail_element = document.getElementById('fail');
 var fail_text = document.querySelector('#fail span');
@@ -144,6 +145,80 @@
 	},
 };
 
+
+function Find() {
+	this.div = document.createElement('div');
+	
+	this.form = document.createElement('form');
+	this.div.appendChild(this.form);
+	
+	var para = addParaWithText(this.form, lang.enter_query);
+	para.appendChild(document.createElement('br'));
+	this.input = document.createElement('input');
+	this.input.type = 'text';
+	this.input.style.width = '80%';
+	para.appendChild(this.input);
+	para.appendChild(document.createElement('hr'));
+	
+	this.results = document.createElement('div');
+	this.div.appendChild(this.results);
+	
+	this.input.addEventListener('keyup', this.findDelay.bind(this));
+	this.form.addEventListener('submit', this.findDelay.bind(this));
+}
+Find.prototype = {
+	query: '',
+	timeout: null,
+	
+	div: null,
+	form: null,
+	input: null,
+	results: null,
+	
+	find: function() {
+		var query = this.input.value.toUpperCase();
+		if(query === this.query) return;
+		this.query = query;
+		
+		var features = [];
+		stops_type.forEach(function(stop_type) {
+			if(stop_type.substr(0,1) === 'p') return;
+			stops_source[stop_type].forEachFeature(function(feature) {
+				if(feature.get('name').toUpperCase().indexOf(query) > -1) {
+					features.push(feature);
+				}
+			});
+		});
+		
+		ttss_types.forEach(function(ttss_type) {
+			vehicles_source[ttss_type].forEachFeature(function(feature) {
+				if(feature.get('vehicle_type') && feature.get('vehicle_type').num.indexOf(query) > -1) {
+					features.push(feature);
+				}
+			});
+		});
+		
+		deleteChildren(this.results);
+		this.results.appendChild(listFeatures(features));
+	},
+	findDelay: function(e) {
+		e.preventDefault();
+		if(this.timeout) clearTimeout(this.timeout);
+		this.timeout = setTimeout(this.find.bind(this), 100);
+	},
+	open: function(panel) {
+		ignore_hashchange = true;
+		window.location.hash = '#!f';
+		
+		panel.show(this.div, this.close.bind(this));
+		this.input.focus();
+	},
+	close: function() {
+		if(this.timeout) clearTimeout(this.timeout);
+	},
+};
+
+
 function fail(msg) {
 	setText(fail_text, msg);
 	fail_element.style.top = '0.5em';
@@ -193,7 +268,7 @@
 		fill = '#05B';
 	}
 	if(selected) {
-		fill = '#292';
+		fill = '#922';
 	}
 	
 	var image = '<svg xmlns="http://www.w3.org/2000/svg" height="30" width="20"><polygon points="10,0 20,23 0,23" style="fill:'+fill+';stroke:'+color_type+';stroke-width:3" /></svg>';
@@ -228,7 +303,6 @@
 	
 	var feature, prefix;
 	for(var i = 0; i < stops.length; i++) {
-		feature = null;
 		if(stops[i].getId) {
 			feature = stops[i];
 		} else {
@@ -310,8 +384,9 @@
 function updateStopSource(stops, prefix) {
 	var source = stops_source[prefix];
 	var mapping = stops_mapping[prefix];
+	var stop;
 	for(var i = 0; i < stops.length; i++) {
-		var stop = stops[i];
+		stop = stops[i];
 		
 		if(stop.category == 'other') continue;
 		if(stops_ignored.includes(stop.shortName)) continue;
@@ -347,7 +422,7 @@
 	}).fail(fail_ajax);
 }
 
-function vehiclePath(feature, tripId) {
+function vehiclePath(feature) {
 	if(path_xhr) path_xhr.abort();
 	
 	var featureId = feature.getId();
@@ -484,11 +559,8 @@
 		return;
 	}
 	
-	var coordinates = feature.getGeometry().getCoordinates();
-	
 	var div = document.createElement('div');
 	
-	var typeName;
 	var name = normalizeName(feature.get('name'));
 	var additional;
 	var table = document.createElement('table');
@@ -586,7 +658,19 @@
 	loader.colSpan = thead.childNodes.length;
 	
 	addParaWithText(div, typeName).className = 'type';
-	addParaWithText(div, name).className = 'name';
+	
+	var nameElement = addParaWithText(div, name + ' ');
+	nameElement.className = 'name';
+	
+	var showOnMapElement = addElementWithText(nameElement, 'a', lang.show_on_map);
+	var showOnMapFunction = function() {
+		setTimeout(function () {map.getView().animate({
+			center: feature.getGeometry().getCoordinates(),
+		})}, 10);
+	};
+	showOnMapElement.addEventListener('click', showOnMapFunction);
+	showOnMapElement.className = 'icon-pin addon-icon';
+	showOnMapElement.title = lang.show_on_map;
 	
 	if(additional) {
 		div.appendChild(additional);
@@ -598,10 +682,7 @@
 		window.location.hash = '#!' + feature.getId();
 	}
 	
-	setTimeout(function () {map.getView().animate({
-		center: feature.getGeometry().getCoordinates(),
-	}) }, 10);
-	
+	showOnMapFunction();
 	
 	panel.show(div, function() {
 		if(!ignore_hashchange) {
@@ -620,6 +701,41 @@
 	feature_clicked = feature;
 }
 
+function listFeatures(features) {
+	var div = document.createElement('div');
+	
+	addParaWithText(div, lang.select_feature);
+	
+	var feature, p, a, full_type, typeName;
+	for(var i = 0; i < features.length; i++) {
+		feature = features[i];
+		
+		p = document.createElement('p');
+		a = document.createElement('a');
+		p.appendChild(a);
+		a.addEventListener('click', function(feature) { return function() {
+			featureClicked(feature);
+		}}(feature));
+		
+		full_type = feature.getId().match(/^[a-z]+/)[0];
+		typeName = lang.types[full_type];
+		if(typeof typeName === 'undefined') {
+			typeName = '';
+		}
+		if(feature.get('vehicle_type')) {
+			typeName += ' ' + feature.get('vehicle_type').num;
+		}
+		
+		addElementWithText(a, 'span', typeName).className = 'small';
+		a.appendChild(document.createTextNode(' '));
+		addElementWithText(a, 'span', normalizeName(feature.get('name')));
+		
+		div.appendChild(p);
+	}
+	
+	return div;
+}
+
 function mapClicked(e) {
 	var point = e.coordinate;
 	var features = [];
@@ -628,43 +744,14 @@
 		if(feature.getId()) features.push(feature);
 	});
 	
+	var feature = features[0];
+	
 	if(features.length > 1) {
 		featureClicked();
-		
-		var div = document.createElement('div');
-		
-		addParaWithText(div, lang.select_feature);
-		
-		var feature, p, a, full_type, typeName;
-		for(var i = 0; i < features.length; i++) {
-			feature = features[i];
-			
-			p = document.createElement('p');
-			a = document.createElement('a');
-			p.appendChild(a);
-			a.addEventListener('click', function(feature) { return function() {
-				featureClicked(feature);
-			}}(feature));
-			
-			full_type = feature.getId().match(/^[a-z]+/)[0];
-			typeName = lang.types[full_type];
-			if(typeof typeName === 'undefined') {
-				typeName = '';
-			}
-			
-			addElementWithText(a, 'span', typeName).className = 'small';
-			a.appendChild(document.createTextNode(' '));
-			addElementWithText(a, 'span', normalizeName(feature.get('name')));
-			
-			div.appendChild(p);
-		}
-		
-		panel.show(div);
-		
+		panel.show(listFeatures(features));
 		return;
 	}
 	
-	var feature = features[0];
 	if(!feature) {
 		stops_type.forEach(function(type) {
 			if(stops_layer[type].getVisible()) {
@@ -710,6 +797,7 @@
 	}
 }
 
+
 function hash() {
 	if(ignore_hashchange) {
 		ignore_hashchange = false;
@@ -734,6 +822,9 @@
 		stopId = window.location.hash.substr(2,1) + 't' + window.location.hash.substr(3);
 	} else if(window.location.hash.match(/^#![sp][tb]-?[0-9]+$/)) {
 		stopId = window.location.hash.substr(2);
+	} else if(window.location.hash.match(/^#!f$/)) {
+		find.open(panel);
+		return;
 	}
 	
 	if(vehicleId) {
@@ -767,6 +858,7 @@
 
 function init() {
 	panel = new Panel(document.getElementById('panel'));
+	find = new Find();
 	
 	route_source = new ol.source.Vector({
 		features: [],
@@ -804,6 +896,7 @@
 		});
 		vehicles_layer[type] = new ol.layer.Vector({
 			source: vehicles_source[type],
+			renderMode: 'image',
 		});
 		vehicles_last_update[type] = 0;
 	});
@@ -828,9 +921,9 @@
 	geolocation_layer = new ol.layer.Vector({
 		source: geolocation_source,
 	});
-	geolocation_button = document.querySelector('#track button');
+	geolocation_button = document.querySelector('#track');
 	if(!navigator.geolocation) {
-		geolocation_button.classList.add('hidden');
+		geolocation_button.remove();
 	}
 	
 	geolocation = new ol.Geolocation({projection: 'EPSG:3857'});
@@ -855,9 +948,11 @@
 	geolocation.on('error', function(error) {
 		fail(lang.error_location + ' ' + error.message);
 		trackingStop();
-		geolocation_button.classList.add('hidden');
+		geolocation_button.remove();
 	});
 	geolocation_button.addEventListener('click', trackingToggle);
+	
+	document.getElementById('find').addEventListener('click', find.open.bind(find, panel));
 	
 	var layers = [
 		new ol.layer.Tile({
@@ -893,7 +988,7 @@
 				element: fail_element,
 			}),
 			new ol.control.Control({
-				element: document.getElementById('track'),
+				element: document.getElementById('menu'),
 			}),
 		]),
 		loadTilesWhileAnimating: false,
@@ -914,7 +1009,7 @@
 	});
 	
 	// Change layer visibility on zoom
-	var change_resolution = function(e) {
+	var change_resolution = function() {
 		stops_type.forEach(function(type) {
 			if(type.startsWith('p')) {
 				stops_layer[type].setVisible(map.getView().getZoom() >= 16);
@@ -939,11 +1034,15 @@
 	window.addEventListener('hashchange', hash);
 	
 	setTimeout(function() {
-		if(trams_xhr) trams_xhr.abort();
-		if(trams_timer) clearTimeout(trams_timer);
-		if(buses_xhr) buses_xhr.abort();
-		if(buses_timer) clearTimeout(buses_timer);
-		  
+		ttss_types.forEach(function(type) {
+			if(vehicles_xhr[type]) {
+				vehicles_xhr[type].abort();
+			}
+			if(vehicles_timer[type]) {
+				clearTimeout(vehicles_timer[type]);
+			}
+		});
+		
 		fail(lang.error_refresh);
 	}, 1800000);
 }

--
Gitblit v1.9.1