text
text
text
text
text
text
text
text
text
text
After breakfast, you are transferred to Glengoyne Distillery for a tour and tasting session. You are then transferred to Drymen to begin your walk on the West Highland Way, taking in views over Loch Lomond from the top of Conic Hill before descending into Balmaha for your evening stay.
Your walk today takes you along the east side of Loch Lomond, clinging to the water’s edge along much of the way. This is a lovely, low level and relaxed stage as it meanders past pebbly beaches and giving you plenty of time to take in the surrounding scenery and wildlife.
From Rowardennan, the path heads into thick woodland. There are two options here: the easier being the wide forest track which follows the contours up and down the hill; or the more time consuming, narrow path which skirts over rocks and exposed tree roots along the banks of the loch. The rocky and uneven path continues along the loch, hugging the water’s edge amidst ancient oak forest. The loch becomes noticeably narrower as the path approaches Inverarnan.
Soon after leaving Inverarnan, the path runs alongside the picturesque, fast-flowing River Falloch before a long stretch on an old military road. The route here closely follows the West Highland Line railway and eventually passes underneath the tracks as the path nears a coniferous forest. The route continues through the forest along a winding, undulating path, eventually crossing the road and River Fillin. Views of the surrounding mountains are wonderful. A well-maintained path follows the River Cononish before reaching Tyndrum.
Take a steep serpentine path up to the village of Anopolis, from there walk to ancient Aradena, situated at the edge of the impressive Aradena Gorge. A scenic path goes down into the Aradena Gorge all the way to the coast. The last part of today’s walk takes you along the coast back to Loutro. A shorter route today is possible via the tiny village of Livaniana and then descending through the Aradena Gorge to the sea. Depending on your flight time on the following day, you may need to travel back to Chora Sfakion by ferry today, and on to Chania or Heraklion.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Add a 3D model using three.js</title>
<meta property="og:description" content="Use a custom style layer with three.js to add a 3D model to the map." />
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.19.0/dist/maplibre-gl.css' />
<script src='https://unpkg.com/maplibre-gl@5.19.0/dist/maplibre-gl.js'></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.169.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.169.0/examples/jsm/"
}
}
</script>
<div id="map"></div>
<script type="module">
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const map = new maplibregl.Map({
container: 'map',
style: 'https://tiles.openfreemap.org/styles/bright',
zoom: 18,
center: [148.9819, -35.3981],
pitch: 60,
canvasContextAttributes: {antialias: true} // create the gl context with MSAA antialiasing, so custom layers are antialiased
});
// parameters to ensure the model is georeferenced correctly on the map
const modelOrigin = [148.9819, -35.39847];
const modelAltitude = 0;
const modelRotate = [Math.PI / 2, 0, 0];
const modelAsMercatorCoordinate = maplibregl.MercatorCoordinate.fromLngLat(
modelOrigin,
modelAltitude
);
// transformation parameters to position, rotate and scale the 3D model onto the map
const modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* Since our 3D model is in real world meters, a scale transform needs to be
* applied since the CustomLayerInterface expects units in MercatorCoordinates.
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
};
// configuration of the custom layer for a 3D model per the CustomLayerInterface
const customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd (map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();
// create two three.js lights to illuminate the model
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, -70, 100).normalize();
this.scene.add(directionalLight);
const directionalLight2 = new THREE.DirectionalLight(0xffffff);
directionalLight2.position.set(0, 70, 100).normalize();
this.scene.add(directionalLight2);
// use the three.js GLTF loader to add the 3D model to the three.js scene
const loader = new GLTFLoader();
loader.load(
'https://maplibre.org/maplibre-gl-js/docs/assets/34M_17/34M_17.gltf',
(gltf) => {
this.scene.add(gltf.scene);
}
);
this.map = map;
// use the MapLibre GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
this.renderer.autoClear = false;
},
render (gl, args) {
const rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform.rotateX
);
const rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform.rotateY
);
const rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform.rotateZ
);
const m = new THREE.Matrix4().fromArray(args.defaultProjectionData.mainMatrix);
const l = new THREE.Matrix4()
.makeTranslation(
modelTransform.translateX,
modelTransform.translateY,
modelTransform.translateZ
)
.scale(
new THREE.Vector3(
modelTransform.scale,
-modelTransform.scale,
modelTransform.scale
)
)
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
// Alternatively, you can use this API to get the correct model matrix.
// It will work regardless of current projection.
// Also see the example "globe-3d-model.html".
//
// const modelMatrix = args.getMatrixForModel(modelOrigin, modelAltitude);
// const m = new THREE.Matrix4().fromArray(matrix);
// const l = new THREE.Matrix4().fromArray(modelMatrix);
this.camera.projectionMatrix = m.multiply(l);
this.renderer.resetState();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
};
map.on('style.load', () => {
map.addLayer(customLayer);
});
</script>
</body>
</html>