Skip to content
This repository was archived by the owner on Dec 12, 2025. It is now read-only.

Commit 5f2d933

Browse files
Implement map ViewHelper
1 parent a6c57ee commit 5f2d933

File tree

11 files changed

+587
-0
lines changed

11 files changed

+587
-0
lines changed

Classes/Controller/.gitkeep

Whitespace-only changes.

Classes/Domain/Model/.gitkeep

Whitespace-only changes.

Classes/Domain/Repository/.gitkeep

Whitespace-only changes.
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
# This file is part of the VIS extension for TYPO3.
5+
#
6+
# For the full copyright and license information, please read the
7+
# LICENSE.txt file that was distributed with this source code.
8+
9+
10+
namespace Digicademy\VIS\ViewHelpers;
11+
12+
use TYPO3\CMS\Core\Page\AssetCollector;
13+
use TYPO3\CMS\Core\Utility\GeneralUtility;
14+
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
15+
16+
defined('TYPO3') or die();
17+
18+
/**
19+
* ViewHelper to produce a map visualisation based on GeoJSON data
20+
*/
21+
final class MapViewHelper extends AbstractTagBasedViewHelper
22+
{
23+
protected $tagName = 'div';
24+
25+
private AssetCollector $assetCollector;
26+
27+
public function __construct()
28+
{
29+
parent::__construct();
30+
$this->assetCollector = GeneralUtility::makeInstance(AssetCollector::class);
31+
}
32+
33+
public function initializeArguments(): void
34+
{
35+
parent::initializeArguments();
36+
$this->registerArgument(
37+
'id',
38+
'string',
39+
'ID of the map element',
40+
true,
41+
);
42+
$this->registerArgument(
43+
'class',
44+
'string',
45+
'CSS classes of the map element',
46+
false,
47+
'mdlr-frame mdlr-variant-map',
48+
);
49+
$this->registerArgument(
50+
'style',
51+
'string',
52+
'URL of the tile layer to use',
53+
false,
54+
'https://tiles.openfreemap.org/styles/positron',
55+
);
56+
$this->registerArgument(
57+
'centerLatitude',
58+
'float',
59+
'Latitude coordinate to use as the initial center of the map',
60+
false,
61+
8.2704513,
62+
);
63+
$this->registerArgument(
64+
'centerLongitude',
65+
'float',
66+
'Longitude coordinates to use as the initial center of the map',
67+
false,
68+
49.9765528,
69+
);
70+
$this->registerArgument(
71+
'zoom',
72+
'float',
73+
'Initial zoom factor of the map',
74+
false,
75+
9,
76+
);
77+
$this->registerArgument(
78+
'color',
79+
'string',
80+
'Color to apply to initial features',
81+
false,
82+
'#000'
83+
);
84+
$this->registerArgument(
85+
'markers',
86+
'bool',
87+
'Whether to show markers instead of drawing features',
88+
false,
89+
false,
90+
);
91+
$this->registerArgument(
92+
'geoJson',
93+
'string',
94+
'GeoJSON features to show on the map',
95+
false,
96+
'{}',
97+
);
98+
}
99+
100+
public function render(): string
101+
{
102+
// Set variables from arguments
103+
$id = htmlspecialchars($this->arguments['id']);
104+
$class = htmlspecialchars($this->arguments['class']);
105+
$style = htmlspecialchars($this->arguments['style']);
106+
$centerLatitude = $this->arguments['centerLatitude'];
107+
$centerLongitude = $this->arguments['centerLongitude'];
108+
$zoom = $this->arguments['zoom'];
109+
$color = htmlspecialchars($this->arguments['color']);
110+
$markers = $this->arguments['markers'];
111+
$geoJson = json_encode(json_decode($this->arguments['geoJson'], true));
112+
113+
// Compose map-specific JS code
114+
$map = "
115+
/* Base map */
116+
const {$id} = new maplibregl.Map({
117+
container: '{$id}',
118+
style: '{$style}',
119+
center: [{$centerLatitude}, {$centerLongitude}],
120+
zoom: {$zoom},
121+
pitch: 20,
122+
rollEnabled: true,
123+
attributionControl: false,
124+
});
125+
126+
/* Projection */
127+
{$id}.on('style.load', () => {
128+
{$id}.setProjection({
129+
type: 'globe',
130+
});
131+
});
132+
133+
/* Attribution control */
134+
{$id}.addControl(new maplibregl.AttributionControl({
135+
compact: true,
136+
}), 'bottom-left');
137+
138+
/* Fullscreen control */
139+
{$id}.addControl(new maplibregl.FullscreenControl(), 'bottom-right');
140+
141+
/* Navigation controls */
142+
{$id}.addControl(new maplibregl.NavigationControl({
143+
visualizePitch: true,
144+
visualizeRoll: true,
145+
showZoom: true,
146+
showCompass: true,
147+
}), 'bottom-right');
148+
149+
/* Generic function to add a marker */
150+
function visShowMarker(feature) {
151+
152+
/* Add popup */
153+
let popupContent = '';
154+
if(feature.properties.url && feature.properties.name) {
155+
popupContent = '<a href=\"' + feature.properties.url + '\">' + feature.properties.name + '</a>';
156+
} else if(feature.properties.name) {
157+
popupContent = feature.properties.name;
158+
}
159+
let popup = new maplibregl.Popup()
160+
.setHTML(popupContent);
161+
162+
/* Add marker */
163+
let marker = new maplibregl.Marker({
164+
color: '{$color}',
165+
}).setLngLat(feature.geometry.coordinates)
166+
.setPopup(popup)
167+
.addTo({$id});
168+
169+
/* Provide marker for list */
170+
return marker;
171+
}
172+
173+
/* Generic function to add a feature */
174+
function visShowFeature(feature) {
175+
176+
/* Add payload as source */
177+
{$id}.addSource('payload', {
178+
'type': 'geojson',
179+
'data': payload,
180+
});
181+
182+
/* Render source: circles */
183+
{$id}.addLayer({
184+
'id': 'payload',
185+
'type': 'circle',
186+
'source': 'payload',
187+
'paint': {
188+
'circle-radius': 6,
189+
'circle-color': '#B42222',
190+
},
191+
'filter': ['==', '\$type', 'Point']
192+
});
193+
194+
/* Render source */
195+
/*{$id}.addLayer({
196+
'id': 'payload',
197+
'type': 'fill',
198+
'source': 'payload',
199+
'paint': {
200+
'fill-color': '#888888',
201+
'fill-outline-color': 'red',
202+
'fill-opacity': 0.4,
203+
},
204+
// filter for (multi)polygons; for also displaying linestrings
205+
// or points add more layers with different filters
206+
'filter': ['==', '\$type', 'Polygon']
207+
});*/
208+
}
209+
";
210+
211+
// Either render markers from GeoJSON
212+
if($markers) {
213+
$map .= "
214+
/* GeoJSON payload */
215+
const payload = {$geoJson};
216+
let markers = [];
217+
{$id}.on('load', () => {
218+
219+
/* Show individual or multiple markers */
220+
if(payload.geometry) {
221+
markers.push(visShowMarker(payload));
222+
} else if(payload.features) {
223+
payload.features.forEach((feature) => {
224+
markers.push(visShowMarker(feature));
225+
});
226+
}
227+
});
228+
";
229+
230+
// Or render GeoJSON features
231+
} else {
232+
$map .= "
233+
/* GeoJSON payload */
234+
const payload = {$geoJson};
235+
{$id}.on('load', () => {
236+
237+
/* Show individual or multiple features */
238+
visShowFeature(payload);
239+
});
240+
";
241+
}
242+
243+
// Include MapLibre GL JS assets
244+
$this->assetCollector->addJavaScript(
245+
'maplibre-gl-js',
246+
'EXT:vis/Resources/Public/JavaScript/maplibre-gl.js',
247+
[],
248+
['priority' => true],
249+
);
250+
$this->assetCollector->addStyleSheet(
251+
'maplibre-gl-css',
252+
'EXT:vis/Resources/Public/Css/maplibre-gl.css',
253+
['media' => 'all'],
254+
);
255+
256+
// Include map-specific JS code
257+
$this->assetCollector->addInlineJavaScript(
258+
'maplibre-gl-js',
259+
$map,
260+
);
261+
262+
// Create tag
263+
$this->tag->forceClosingTag(true);
264+
$this->tag->addAttribute(
265+
'id',
266+
$id,
267+
);
268+
$this->tag->addAttribute(
269+
'class',
270+
$class,
271+
);
272+
273+
// Render tag
274+
return $this->tag->render();
275+
276+
// TODO Finish GeoJSON rendering
277+
// TODO Fix button und popover styles, maybe icons
278+
// TODO Zoom to features?
279+
}
280+
}

Configuration/TCA/.gitkeep

Whitespace-only changes.

Resources/Private/Language/.gitkeep

Whitespace-only changes.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
Copyright (c) 2023, MapLibre contributors
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without modification,
6+
are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright notice,
9+
this list of conditions and the following disclaimer.
10+
* Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
* Neither the name of MapLibre GL JS nor the names of its contributors
14+
may be used to endorse or promote products derived from this software
15+
without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
29+
30+
-------------------------------------------------------------------------------
31+
32+
Contains code from mapbox-gl-js v1.13 and earlier
33+
34+
Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license
35+
36+
Copyright (c) 2020, Mapbox
37+
Redistribution and use in source and binary forms, with or without modification,
38+
are permitted provided that the following conditions are met:
39+
40+
* Redistributions of source code must retain the above copyright notice,
41+
this list of conditions and the following disclaimer.
42+
* Redistributions in binary form must reproduce the above copyright notice,
43+
this list of conditions and the following disclaimer in the documentation
44+
and/or other materials provided with the distribution.
45+
* Neither the name of Mapbox GL JS nor the names of its contributors
46+
may be used to endorse or promote products derived from this software
47+
without specific prior written permission.
48+
49+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
50+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
51+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
52+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
53+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
54+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
55+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
56+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
57+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
58+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
59+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60+
61+
62+
-------------------------------------------------------------------------------
63+
64+
Contains code from glfx.js
65+
66+
Copyright (C) 2011 by Evan Wallace
67+
68+
Permission is hereby granted, free of charge, to any person obtaining a copy
69+
of this software and associated documentation files (the "Software"), to deal
70+
in the Software without restriction, including without limitation the rights
71+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
72+
copies of the Software, and to permit persons to whom the Software is
73+
furnished to do so, subject to the following conditions:
74+
75+
The above copyright notice and this permission notice shall be included in
76+
all copies or substantial portions of the Software.
77+
78+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
79+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
80+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
81+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
82+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
83+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
84+
THE SOFTWARE.
85+
86+
--------------------------------------------------------------------------------
87+
88+
Contains a portion of d3-color https://github.com/d3/d3-color
89+
90+
Copyright 2010-2016 Mike Bostock
91+
All rights reserved.
92+
93+
Redistribution and use in source and binary forms, with or without modification,
94+
are permitted provided that the following conditions are met:
95+
96+
* Redistributions of source code must retain the above copyright notice, this
97+
list of conditions and the following disclaimer.
98+
99+
* Redistributions in binary form must reproduce the above copyright notice,
100+
this list of conditions and the following disclaimer in the documentation
101+
and/or other materials provided with the distribution.
102+
103+
* Neither the name of the author nor the names of contributors may be used to
104+
endorse or promote products derived from this software without specific prior
105+
written permission.
106+
107+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
108+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
109+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
110+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
111+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
112+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
113+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
114+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
115+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
116+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Resources/Public/Css/maplibre-gl.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)