nbsp; in XML

Im HTML existiert für das geschützte Leerzeichen die HTML-Entity:

nbsp;

Bei bestimmten Frameworks aus dem HTML5-Bereich wie Framework 7 kann diese Entity allerdings nicht genutzt werden, da z.B. Unterseiten als XML interpretiert werden und der Parser in diesem Fall mit einer Fehlermeldung reagieren würde. Stattdessen kann für das geschützte Leerzeichen das Zeichen:

 

genutzt werden. Bei diesem Zeichen handelt es sich ebenfalls um das geschützte Leerzeichen.

Geocoding in Verbindung mit einer Leaflet-App

Gestern schrieb ich in einem Artikel, wie Adressen aus Koordinaten mittels des Dienstes Nominatim, unter Zuhilfenahme von OpenStreetMap-Daten, ermittelt werden können. Mithilfe der freien Bibliothek Leafleat, kann dies nun in eine kleine Webapplikation gegossen werden.

Anhand der Geo-Koordinate wird die Adresse ermittelt und dargestellt.

Ziel ist es eine Applikation zu bauen, welche eine Karte darstellt und bei einem Klick auf diese Karte den Geocoding-Dienst anfragt. Sobald die Antwort vom Dienst eingetroffen ist, soll in einem Pop-up die Adresse dargestellt werden. Nachdem ein Grundgerüst in Form einer HTML-Datei erstellt wurde, werden dort die benötigten Bibliotheken (Leaflet und jQuery) eingebunden. Anschließend kann die Karte erzeugt werden. Damit diese im Vollbild dargestellt wird, findet sich eine entsprechende CSS-Definition in der Datei:

<style type="text/css">
	body {
		padding: 0;
		margin: 0;
	}

	html, body, #map {
		height: 100%;
	}
</style>

Anschließend kann der TileLayer angelegt werden und der Map zugewiesen werden:

// Options
var centerLatitude=53.49577;
var centerLongitude=13.30873;
var zoomLevel=15;

// Define layer
var openStreetMapLayer = new L.TileLayer(
		'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
		{attribution: '<a href="https://www.openstreetmap.org/copyright">© OpenStreetMap contributors</a>'});

// Define map
var map = L.map('map', {
	center: new L.LatLng(centerLatitude, centerLongitude),
	zoom: zoomLevel,
	layers: [openStreetMapLayer]
});

Für die Karte muss im nächsten Schritt ein Click-Event definiert werden, welches den AJAX-Request gegen den Geocoding-Dienst auslöst und den Marker erstellt. Der jeweilige Marker wird in eine separate Ebene gepackt und beim nächsten Klick wieder gelöscht und anschließend ein neuer Marker erstellt:

// Marker managment
var markerLayer;
map.on('click', function(e){

	// Console debug
	var coord = e.latlng;
	var lat = coord.lat;
	var lng = coord.lng;
	console.log("You clicked the map at latitude: " + lat + " and longitude: " + lng);

	// Marker managment		
	if(markerLayer != undefined) {
		map.removeLayer(markerLayer);
	}
	
	markerLayer = new L.Marker(e.latlng);
	map.addLayer(markerLayer);

	// Call geocode api
	var request =	$.ajax({
		method: "GET",
		url: "https://nominatim.openstreetmap.org/reverse?lat=" + lat + "&lon=" + lng + "&format=json"
	});

	request.done(function( json ) {
		console.log(json);
		markerLayer.bindPopup(json.display_name).openPopup();
	});
});

Am Stück sieht die Applikation wie folgt aus:

<!DOCTYPE html>
<html>
<head>
	<title>Leaflet address encoder</title>
	<meta charset="utf-8" />

	<meta name="viewport" content="width=device-width, initial-scale=1.0">

	<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
   integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
   crossorigin=""/>
	
	<style type="text/css">
		body {
			padding: 0;
			margin: 0;
		}

		html, body, #map {
			height: 100%;
		}
	</style>
	
	<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
   integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
   crossorigin=""></script>
	<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
	<div id="map"></div>
	
	<script>
	        // Options
		var centerLatitude=53.49577;
		var centerLongitude=13.30873;
		var zoomLevel=15;
		
		// Define layer
		var openStreetMapLayer = new L.TileLayer(
				'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
				{attribution: '<a href="https://www.openstreetmap.org/copyright">© OpenStreetMap contributors</a>'});
		
		// Define map
		var map = L.map('map', {
		    center: new L.LatLng(centerLatitude, centerLongitude),
		    zoom: zoomLevel,
		    layers: [openStreetMapLayer]
		});
		
		// Marker managment
		var markerLayer;
		map.on('click', function(e){

			// Console debug
			var coord = e.latlng;
			var lat = coord.lat;
			var lng = coord.lng;
			console.log("You clicked the map at latitude: " + lat + " and longitude: " + lng);

			// Marker managment		
			if(markerLayer != undefined) {
				map.removeLayer(markerLayer);
			}
			
			markerLayer = new L.Marker(e.latlng);
       		map.addLayer(markerLayer);

			// Call geocode api
			var request =	$.ajax({
				method: "GET",
				url: "https://nominatim.openstreetmap.org/reverse?lat=" + lat + "&lon=" + lng + "&format=json"
			});

			request.done(function( json ) {
				console.log(json);
				markerLayer.bindPopup(json.display_name).openPopup();
			});
  		});
	</script>
</body>
</html>

Alternativ kann der Quelltext heruntergeladen bzw. ausprobiert werden.

Aufzeichnung von Problemen unter Windows 10

Wenn ein Problem unter Windows 10 beschrieben werden soll, kann der Anwender natürlich viel Prosa schreiben. Allerdings ist dies nicht nötig, vor allem wenn es sich um komplizierte Fälle handelt. In Windows 10 existiert für diesen Anwendungsfall eine Anwendung mit dem Namen Schrittaufzeichnung. Unter älteren Version wie Windows 7 ist der Vorgänger dieses Werkzeuges unter dem Namen Problemaufzeichnung zu finden.

Die Schrittaufzeichnung unter Windows 10

Mit dieser Anwendung ist es möglich die Schritte, die zu dem Problem führen, aufzuzeichnen. Dazu wird die Aufzeichnung gestartet und die jeweiligen Schritte können durchgeführt werden. Daneben ist es möglich zu jedem Schritt Kommentare zu hinterlegen. Als Ergebnis erhält der Nutzer eine automatisierte textuelle Beschreibung, welche mit Screenshots und Kommentaren angereichert ist. Abgespeichert wird das Ganze anschließend als mht-Datei, bei welcher es sich um eine einfache HTML-Datei mit integrierten Bildern handeln. Dank der Schrittaufzeichnung kann ein Problem bzw. die Schritte zu diesem schnell beschrieben werden.

Thymeleaf ohne Servlet in Standalone-Anwendungen nutzen

Thymeleaf ist eine Template Engine, welche für die Nutzung mittels Java (oder anderer JVM-Sprachen) gedacht ist. Über diese können entsprechende Templates geschrieben werden, anschließend gefüllt und genutzt werden. Thymeleaf verfügt über eine Dokumentation, allerdings fehlt mir dort ein minimales Beispiel für Anwendungen, welche sich nicht im Web (sogenannte non-web environments) abspielen.

thymeleaf.org

Für ein minimales Beispiel wird natürlich ein Template benötigt. Dieses wird hier als HTML definiert:

<!DOCTYPE html>
<html>
<head>
    <title th:text="${title}"></title>
</head>
<body>
</body>
</html>

Interessant ist hierbei das Feld title, welches wir mit einem Titel füllen wollen. Im Java-Quellcode sieht die Implementation wie folgt aus:

ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setPrefix("templates/");
resolver.setTemplateMode("HTML5");
resolver.setSuffix(".html");

TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(resolver);

Context context = new Context(Locale.GERMAN);
context.setVariable("title", "Ein minimales Beispiel");

final String content = templateEngine.process("header.html", context);

Im ersten Schritt wird mit dem ClassLoaderTemplateResolver ein Resolver definiert, welcher schlussendlich dafür sorgt die Templates aus den Ressourcen (im resources-Ordner) bezogen werden. Daneben wird der Template-Modus eingestellt. Neben dem Modus HTML5 unterstützt Thymeleaf wie z.B. TEXT oder JAVASCRIPT. Danach wird die Template Engine erstellt und ihr der Resolver zugewiesen. Die Variablen, welche in diesem Fall im Template genutzt werden, werden über den Context definiert und gesetzt. Anschließend wird das Ganze an die process-Methode der Template Engine übergeben. Als Ergebnis erhalten wir im String content folgenden Inhalt:

<!DOCTYPE html>
<html>
<head>
    <title">Ein minimales Beispiel</title>
</head>
<body>
</body>
</html>

Damit wurde Thymeleaf in einer minimalen Variante angewendet. Die offizielle Seite der Template Engine ist unter thymeleaf.org zu finden. Lizenziert ist Thymeleaf unter der Apache License in der Version 2.0 und damit freie Software.