1
2
3
4
5
6 package org.tailormap.api.configuration.dev;
7
8 import static org.tailormap.api.persistence.Configuration.HOME_PAGE;
9 import static org.tailormap.api.persistence.Configuration.PORTAL_MENU;
10 import static org.tailormap.api.persistence.json.GeoServiceProtocol.QUANTIZEDMESH;
11 import static org.tailormap.api.persistence.json.GeoServiceProtocol.TILES3D;
12 import static org.tailormap.api.persistence.json.GeoServiceProtocol.WMS;
13 import static org.tailormap.api.persistence.json.GeoServiceProtocol.WMTS;
14 import static org.tailormap.api.persistence.json.GeoServiceProtocol.XYZ;
15 import static org.tailormap.api.persistence.json.HiddenLayerFunctionalityEnum.ATTRIBUTE_LIST;
16 import static org.tailormap.api.persistence.json.HiddenLayerFunctionalityEnum.EXPORT;
17 import static org.tailormap.api.persistence.json.HiddenLayerFunctionalityEnum.FEATURE_INFO;
18 import static org.tailormap.api.security.AuthorisationService.ACCESS_TYPE_VIEW;
19
20 import com.fasterxml.jackson.core.JsonProcessingException;
21 import com.fasterxml.jackson.databind.ObjectMapper;
22 import java.io.IOException;
23 import java.lang.invoke.MethodHandles;
24 import java.nio.charset.StandardCharsets;
25 import java.sql.SQLException;
26 import java.time.OffsetDateTime;
27 import java.time.ZoneId;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.NoSuchElementException;
33 import java.util.Optional;
34 import java.util.Properties;
35 import java.util.Set;
36 import java.util.UUID;
37 import org.apache.solr.client.solrj.SolrServerException;
38 import org.quartz.SchedulerException;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41 import org.springframework.beans.factory.annotation.Value;
42 import org.springframework.boot.SpringApplication;
43 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
44 import org.springframework.boot.context.event.ApplicationReadyEvent;
45 import org.springframework.context.ApplicationContext;
46 import org.springframework.context.event.EventListener;
47 import org.springframework.core.io.ClassPathResource;
48 import org.springframework.jdbc.core.simple.JdbcClient;
49 import org.springframework.transaction.annotation.Transactional;
50 import org.springframework.util.PropertyPlaceholderHelper;
51 import org.tailormap.api.admin.model.TaskSchedule;
52 import org.tailormap.api.geotools.featuresources.AttachmentsHelper;
53 import org.tailormap.api.geotools.featuresources.FeatureSourceFactoryHelper;
54 import org.tailormap.api.geotools.featuresources.JDBCFeatureSourceHelper;
55 import org.tailormap.api.geotools.featuresources.WFSFeatureSourceHelper;
56 import org.tailormap.api.persistence.Application;
57 import org.tailormap.api.persistence.Catalog;
58 import org.tailormap.api.persistence.Configuration;
59 import org.tailormap.api.persistence.GeoService;
60 import org.tailormap.api.persistence.Group;
61 import org.tailormap.api.persistence.Page;
62 import org.tailormap.api.persistence.SearchIndex;
63 import org.tailormap.api.persistence.TMFeatureSource;
64 import org.tailormap.api.persistence.TMFeatureType;
65 import org.tailormap.api.persistence.Upload;
66 import org.tailormap.api.persistence.User;
67 import org.tailormap.api.persistence.helper.GeoServiceHelper;
68 import org.tailormap.api.persistence.json.AppContent;
69 import org.tailormap.api.persistence.json.AppLayerSettings;
70 import org.tailormap.api.persistence.json.AppSettings;
71 import org.tailormap.api.persistence.json.AppTreeLayerNode;
72 import org.tailormap.api.persistence.json.AppTreeLevelNode;
73 import org.tailormap.api.persistence.json.AppTreeNode;
74 import org.tailormap.api.persistence.json.AppUiSettings;
75 import org.tailormap.api.persistence.json.AttachmentAttributeType;
76 import org.tailormap.api.persistence.json.AttributeSettings;
77 import org.tailormap.api.persistence.json.AttributeValueSettings;
78 import org.tailormap.api.persistence.json.AuthorizationRule;
79 import org.tailormap.api.persistence.json.AuthorizationRuleDecision;
80 import org.tailormap.api.persistence.json.Bounds;
81 import org.tailormap.api.persistence.json.CatalogNode;
82 import org.tailormap.api.persistence.json.FeatureTypeRef;
83 import org.tailormap.api.persistence.json.FeatureTypeTemplate;
84 import org.tailormap.api.persistence.json.Filter;
85 import org.tailormap.api.persistence.json.FilterEditConfiguration;
86 import org.tailormap.api.persistence.json.FilterGroup;
87 import org.tailormap.api.persistence.json.GeoServiceDefaultLayerSettings;
88 import org.tailormap.api.persistence.json.GeoServiceLayerSettings;
89 import org.tailormap.api.persistence.json.GeoServiceSettings;
90 import org.tailormap.api.persistence.json.JDBCConnectionProperties;
91 import org.tailormap.api.persistence.json.MenuItem;
92 import org.tailormap.api.persistence.json.PageTile;
93 import org.tailormap.api.persistence.json.ServiceAuthentication;
94 import org.tailormap.api.persistence.json.TailormapObjectRef;
95 import org.tailormap.api.persistence.json.TileLayerHiDpiMode;
96 import org.tailormap.api.repository.ApplicationRepository;
97 import org.tailormap.api.repository.CatalogRepository;
98 import org.tailormap.api.repository.ConfigurationRepository;
99 import org.tailormap.api.repository.FeatureSourceRepository;
100 import org.tailormap.api.repository.GeoServiceRepository;
101 import org.tailormap.api.repository.GroupRepository;
102 import org.tailormap.api.repository.PageRepository;
103 import org.tailormap.api.repository.SearchIndexRepository;
104 import org.tailormap.api.repository.UploadRepository;
105 import org.tailormap.api.repository.UserRepository;
106 import org.tailormap.api.scheduling.IndexTask;
107 import org.tailormap.api.scheduling.TMJobDataMap;
108 import org.tailormap.api.scheduling.Task;
109 import org.tailormap.api.scheduling.TaskManagerService;
110 import org.tailormap.api.scheduling.TaskType;
111 import org.tailormap.api.security.InternalAdminAuthentication;
112 import org.tailormap.api.solr.SolrHelper;
113 import org.tailormap.api.solr.SolrService;
114 import org.tailormap.api.viewer.model.AppStyling;
115 import org.tailormap.api.viewer.model.Component;
116 import org.tailormap.api.viewer.model.ComponentConfig;
117
118
119
120
121
122 @org.springframework.context.annotation.Configuration
123 @ConditionalOnProperty(name = "tailormap-api.database.populate-testdata", havingValue = "true")
124 public class PopulateTestData {
125
126 private static final Logger logger =
127 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
128 private final ApplicationContext appContext;
129 private final UserRepository userRepository;
130 private final GroupRepository groupRepository;
131 private final CatalogRepository catalogRepository;
132 private final GeoServiceRepository geoServiceRepository;
133 private final GeoServiceHelper geoServiceHelper;
134 private final SolrService solrService;
135 private final TaskManagerService taskManagerService;
136 private final FeatureSourceRepository featureSourceRepository;
137 private final ApplicationRepository applicationRepository;
138 private final ConfigurationRepository configurationRepository;
139 private final SearchIndexRepository searchIndexRepository;
140 private final FeatureSourceFactoryHelper featureSourceFactoryHelper;
141 private final UploadRepository uploadRepository;
142 private final PageRepository pageRepository;
143 private final JdbcClient jdbcClient;
144
145 @Value("${spatial.dbs.connect:false}")
146 private boolean connectToSpatialDbs;
147
148 @Value("#{'${tailormap-api.database.populate-testdata.categories}'.split(',')}")
149 private Set<String> categories;
150
151 @Value("${spatial.dbs.localhost:true}")
152 private boolean connectToSpatialDbsAtLocalhost;
153
154 @Value("${tailormap-api.database.populate-testdata.admin-hashed-password}")
155 private String adminHashedPassword;
156
157 @Value("${tailormap-api.database.populate-testdata.exit:false}")
158 private boolean exit;
159
160 @Value("${MAP5_URL:#{null}}")
161 private String map5url;
162
163 private final String tiles3dProxyUsername = "b3p";
164
165
166 private final String tiles3dProxyPassword = "cOxPlJFWmtndFk0eVg5VdL";
167
168 @Value("${tailormap-api.solr-batch-size:1000}")
169 private int solrBatchSize;
170
171 @Value("${tailormap-api.solr-geometry-validation-rule:repairBuffer0}")
172 private String solrGeometryValidationRule;
173
174 public PopulateTestData(
175 ApplicationContext appContext,
176 UserRepository userRepository,
177 GroupRepository groupRepository,
178 CatalogRepository catalogRepository,
179 GeoServiceRepository geoServiceRepository,
180 GeoServiceHelper geoServiceHelper,
181 SolrService solrService,
182 TaskManagerService taskManagerService,
183 FeatureSourceRepository featureSourceRepository,
184 ApplicationRepository applicationRepository,
185 ConfigurationRepository configurationRepository,
186 FeatureSourceFactoryHelper featureSourceFactoryHelper,
187 SearchIndexRepository searchIndexRepository,
188 UploadRepository uploadRepository,
189 PageRepository pageRepository,
190 JdbcClient jdbcClient) {
191 this.appContext = appContext;
192 this.userRepository = userRepository;
193 this.groupRepository = groupRepository;
194 this.catalogRepository = catalogRepository;
195 this.geoServiceRepository = geoServiceRepository;
196 this.geoServiceHelper = geoServiceHelper;
197 this.solrService = solrService;
198 this.taskManagerService = taskManagerService;
199 this.featureSourceRepository = featureSourceRepository;
200 this.applicationRepository = applicationRepository;
201 this.configurationRepository = configurationRepository;
202 this.featureSourceFactoryHelper = featureSourceFactoryHelper;
203 this.searchIndexRepository = searchIndexRepository;
204 this.uploadRepository = uploadRepository;
205 this.pageRepository = pageRepository;
206 this.jdbcClient = jdbcClient;
207 }
208
209 @EventListener(ApplicationReadyEvent.class)
210 @Transactional
211 public void populate() throws Exception {
212 InternalAdminAuthentication.setInSecurityContext();
213 try {
214
215
216 createTestUsersAndGroups();
217 createConfigurationTestData();
218 if (categories.contains("catalog")) {
219 createCatalogTestData();
220 }
221 if (categories.contains("apps")) {
222 createAppTestData();
223 }
224 if (categories.contains("search-index")) {
225 try {
226 createSolrIndex();
227 } catch (Exception e) {
228 logger.error("Exception creating Solr Index for testdata (continuing)", e);
229 }
230 }
231 if (categories.contains("search-index")) {
232 createSearchIndexTasks();
233 }
234 if (categories.contains("pages")) {
235 createPages();
236 }
237 logger.info("Test entities created");
238 if (categories.contains("drawing")) {
239 insertTestDrawing();
240 logger.info("Test drawing created");
241 try {
242 insertDrawingStyle();
243 logger.info("Test drawing style created");
244 } catch (Exception e) {
245 logger.error("Exception creating drawing style", e);
246 }
247 }
248 } finally {
249 InternalAdminAuthentication.clearSecurityContextAuthentication();
250 }
251 if (exit) {
252
253
254 new Thread(() -> {
255 try {
256 logger.info("Exiting in 10 seconds");
257 Thread.sleep(10000);
258 } catch (InterruptedException ignored) {
259
260 }
261 SpringApplication.exit(appContext, () -> 0);
262 System.exit(0);
263 })
264 .start();
265 }
266 }
267
268 public void createTestUsersAndGroups() throws NoSuchElementException {
269 Group groupFoo = new Group().setName("test-foo").setDescription("Used for integration tests.");
270 groupRepository.save(groupFoo);
271
272 Group groupBar = new Group().setName("test-bar").setDescription("Used for integration tests.");
273 groupBar.addOrUpdateAdminProperty("group-property", true, true);
274 groupBar.addOrUpdateAdminProperty("group-private-property", 999.9, false);
275 groupRepository.save(groupBar);
276
277 Group groupBaz = new Group().setName("test-baz").setDescription("Used for integration tests.");
278 groupRepository.save(groupBaz);
279
280
281 User u = new User().setUsername("user").setPassword("{noop}user").setEmail("user@example.com");
282 u.getGroups().addAll(List.of(groupFoo, groupBar, groupBaz));
283 userRepository.save(u);
284
285
286 u = new User()
287 .setUsername("foo")
288 .setPassword("{noop}foo")
289 .setEmail("foo@example.com")
290 .setName("Foo only user");
291 u.getGroups().add(groupFoo);
292 userRepository.save(u);
293
294 u = new User()
295 .setUsername("disabled")
296 .setPassword("{noop}disabled")
297 .setEmail("disabled@example.com")
298 .setEnabled(false);
299 userRepository.save(u);
300
301 u = new User()
302 .setUsername("expired")
303 .setPassword("{noop}expired")
304 .setEmail("expired@example.com")
305 .setValidUntil(
306 OffsetDateTime.now(ZoneId.systemDefault()).minusDays(1).toZonedDateTime());
307 userRepository.save(u);
308
309
310 u = new User().setUsername("tm-admin").setPassword(adminHashedPassword);
311 u.addOrUpdateAdminProperty("some-property", "some-value", true);
312 u.addOrUpdateAdminProperty("admin-property", "private-value", false);
313 u.getGroups().add(groupRepository.findById(Group.ADMIN).orElseThrow());
314 u.getGroups().add(groupBar);
315 userRepository.save(u);
316 }
317
318 private final List<AuthorizationRule> ruleAnonymousRead = List.of(new AuthorizationRule()
319 .groupName(Group.ANONYMOUS)
320 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)));
321
322 private final List<AuthorizationRule> ruleLoggedIn = List.of(new AuthorizationRule()
323 .groupName(Group.AUTHENTICATED)
324 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)));
325
326 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
327 private void createCatalogTestData() throws Exception {
328 Catalog catalog = catalogRepository.findById(Catalog.MAIN).orElseThrow();
329 CatalogNode rootCatalogNode = catalog.getNodes().getFirst();
330 CatalogNode catalogNode = new CatalogNode().id("test").title("Test services");
331 rootCatalogNode.addChildrenItem(catalogNode.getId());
332 catalog.getNodes().add(catalogNode);
333
334 String osmAttribution = "© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors";
335
336 Bounds rdTileGridExtent =
337 new Bounds().minx(-285401.92).maxx(595401.92).miny(22598.08).maxy(903401.92);
338
339 Upload legend = new Upload()
340 .setCategory(Upload.CATEGORY_LEGEND)
341 .setFilename("gemeentegebied-legend.png")
342 .setMimeType("image/png")
343 .setContent(new ClassPathResource("test/gemeentegebied-legend.png").getContentAsByteArray())
344 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
345 uploadRepository.save(legend);
346
347 Collection<GeoService> services = List.of(
348 new GeoService()
349 .setId("demo")
350 .setProtocol(WMS)
351 .setTitle("Demo")
352 .setPublished(true)
353 .setAuthorizationRules(ruleAnonymousRead)
354 .setUrl("https://demo.tailormap.com/geoserver/geodata/ows?SERVICE=WMS"),
355 new GeoService()
356 .setId("osm")
357 .setProtocol(XYZ)
358 .setTitle("OSM")
359 .setUrl("https://tile.openstreetmap.org/{z}/{x}/{y}.png")
360 .setAuthorizationRules(ruleAnonymousRead)
361 .setSettings(new GeoServiceSettings()
362 .xyzCrs("EPSG:3857")
363 .layerSettings(Map.of(
364 "xyz",
365 new GeoServiceLayerSettings()
366 .attribution(osmAttribution)
367 .maxZoom(19)))),
368
369 new GeoService()
370 .setId("snapshot-geoserver")
371 .setProtocol(WMS)
372 .setTitle("Test GeoServer")
373 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
374 .setAuthorizationRules(ruleAnonymousRead)
375 .setPublished(true),
376 new GeoService()
377 .setId("filtered-snapshot-geoserver")
378 .setProtocol(WMS)
379 .setTitle("Test GeoServer (with authorization rules)")
380 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
381 .setAuthorizationRules(List.of(
382 new AuthorizationRule()
383 .groupName("test-foo")
384 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)),
385 new AuthorizationRule()
386 .groupName("test-baz")
387 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))
388 .setSettings(new GeoServiceSettings()
389 .layerSettings(Map.of(
390 "BGT",
391 new GeoServiceLayerSettings()
392 .addAuthorizationRulesItem(new AuthorizationRule()
393 .groupName("test-foo")
394 .decisions(Map.of(
395 ACCESS_TYPE_VIEW, AuthorizationRuleDecision.DENY)))
396 .addAuthorizationRulesItem(new AuthorizationRule()
397 .groupName("test-baz")
398 .decisions(Map.of(
399 ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))))
400 .setPublished(true),
401 new GeoService()
402 .setId("snapshot-geoserver-proxied")
403 .setProtocol(WMS)
404 .setTitle("Test GeoServer (proxied)")
405 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
406 .setAuthorizationRules(ruleAnonymousRead)
407 .setSettings(new GeoServiceSettings().useProxy(true)),
408 new GeoService()
409 .setId("openbasiskaart")
410 .setProtocol(WMTS)
411 .setTitle("Openbasiskaart")
412 .setUrl("https://www.openbasiskaart.nl/mapcache/wmts")
413 .setAuthorizationRules(ruleAnonymousRead)
414 .setSettings(new GeoServiceSettings()
415 .defaultLayerSettings(new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
416 .layerSettings(Map.of(
417 "osm",
418 new GeoServiceLayerSettings()
419 .title("Openbasiskaart")
420 .hiDpiDisabled(false)
421 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
422 .hiDpiSubstituteLayer("osm-hq")))),
423 new GeoService()
424 .setId("openbasiskaart-proxied")
425 .setProtocol(WMTS)
426 .setTitle("Openbasiskaart (proxied)")
427 .setUrl("https://www.openbasiskaart.nl/mapcache/wmts")
428 .setAuthorizationRules(ruleAnonymousRead)
429
430
431 .setAuthentication(new ServiceAuthentication()
432 .method(ServiceAuthentication.MethodEnum.PASSWORD)
433 .username("test")
434 .password("test"))
435 .setSettings(new GeoServiceSettings()
436 .useProxy(true)
437 .defaultLayerSettings(new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
438 .layerSettings(Map.of(
439 "osm",
440 new GeoServiceLayerSettings()
441 .hiDpiDisabled(false)
442 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
443 .hiDpiSubstituteLayer("osm-hq")))),
444 new GeoService()
445 .setId("openbasiskaart-tms")
446 .setProtocol(XYZ)
447 .setTitle("Openbasiskaart (TMS)")
448 .setUrl("https://openbasiskaart.nl/mapcache/tms/1.0.0/osm@rd/{z}/{x}/{-y}.png")
449 .setAuthorizationRules(ruleAnonymousRead)
450 .setSettings(
451 new GeoServiceSettings()
452 .xyzCrs("EPSG:28992")
453 .defaultLayerSettings(
454 new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
455 .layerSettings(
456 Map.of(
457 "xyz",
458 new GeoServiceLayerSettings()
459 .maxZoom(15)
460 .tileGridExtent(rdTileGridExtent)
461 .hiDpiDisabled(false)
462 .hiDpiMode(
463 TileLayerHiDpiMode
464 .SUBSTITUTELAYERTILEPIXELRATIOONLY)
465 .hiDpiSubstituteLayer(
466 "https://openbasiskaart.nl/mapcache/tms/1.0.0/osm-hq@rd-hq/{z}/{x}/{-y}.png")))),
467 new GeoService()
468 .setId("pdok-hwh-luchtfotorgb")
469 .setProtocol(WMTS)
470 .setTitle("PDOK HWH luchtfoto")
471 .setUrl("https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0")
472 .setAuthorizationRules(ruleAnonymousRead)
473 .setPublished(true)
474 .setSettings(new GeoServiceSettings()
475 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
476 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl)")
477 .hiDpiDisabled(false))
478 .putLayerSettingsItem(
479 "Actueel_orthoHR", new GeoServiceLayerSettings().title("Luchtfoto"))),
480 new GeoService()
481 .setId("b3p-mapproxy-luchtfoto")
482 .setProtocol(XYZ)
483 .setTitle("Luchtfoto (TMS)")
484 .setUrl("https://mapproxy.b3p.nl/tms/1.0.0/luchtfoto/EPSG28992/{z}/{x}/{-y}.jpeg")
485 .setAuthorizationRules(ruleAnonymousRead)
486 .setPublished(true)
487 .setSettings(new GeoServiceSettings()
488 .xyzCrs("EPSG:28992")
489 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
490 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl)")
491 .hiDpiDisabled(false))
492 .layerSettings(Map.of(
493 "xyz",
494 new GeoServiceLayerSettings()
495 .maxZoom(14)
496 .tileGridExtent(rdTileGridExtent)
497 .hiDpiMode(TileLayerHiDpiMode.SHOWNEXTZOOMLEVEL)))),
498 new GeoService()
499 .setId("at-basemap")
500 .setProtocol(WMTS)
501 .setTitle("basemap.at")
502 .setUrl("https://mapsneu.wien.gv.at/basemapneu/1.0.0/WMTSCapabilities.xml")
503 .setAuthorizationRules(ruleAnonymousRead)
504 .setPublished(true)
505 .setSettings(new GeoServiceSettings()
506 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
507 .attribution("© [basemap.at](https://basemap.at)")
508 .hiDpiDisabled(true))
509 .layerSettings(Map.of(
510 "geolandbasemap",
511 new GeoServiceLayerSettings()
512 .title("Basemap")
513 .hiDpiDisabled(false)
514 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERTILEPIXELRATIOONLY)
515 .hiDpiSubstituteLayer("bmaphidpi"),
516 "bmaporthofoto30cm",
517 new GeoServiceLayerSettings()
518 .title("Orthophoto")
519 .hiDpiDisabled(false)))),
520 new GeoService()
521 .setId("pdok-kadaster-bestuurlijkegebieden")
522 .setProtocol(WMS)
523 .setUrl("https://service.pdok.nl/kadaster/bestuurlijkegebieden/wms/v1_0?service=WMS")
524 .setAuthorizationRules(ruleAnonymousRead)
525 .setSettings(new GeoServiceSettings()
526 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
527 .description("This layer shows an administrative boundary."))
528
529 .serverType(GeoServiceSettings.ServerTypeEnum.MAPSERVER)
530 .useProxy(true)
531 .putLayerSettingsItem(
532 "Gemeentegebied",
533 new GeoServiceLayerSettings()
534 .legendImageId(legend.getId().toString())))
535 .setPublished(true)
536 .setTitle("PDOK Kadaster bestuurlijke gebieden"),
537 new GeoService()
538 .setId("bestuurlijkegebieden-proxied")
539 .setProtocol(WMS)
540 .setUrl("https://service.pdok.nl/kadaster/bestuurlijkegebieden/wms/v1_0?service=WMS")
541 .setAuthorizationRules(ruleAnonymousRead)
542
543
544
545 .setAuthentication(new ServiceAuthentication()
546 .method(ServiceAuthentication.MethodEnum.PASSWORD)
547 .username("test")
548 .password("test"))
549 .setSettings(new GeoServiceSettings()
550
551 .serverType(GeoServiceSettings.ServerTypeEnum.MAPSERVER)
552 .useProxy(true))
553 .setPublished(true)
554 .setTitle("Bestuurlijke gebieden (proxied met auth)"),
555 new GeoService()
556 .setId("3dbag_utrecht")
557 .setProtocol(TILES3D)
558 .setUrl("https://3dtilesnederland.nl/tiles/1.0/implicit/nederland/344.json")
559 .setTitle("3D BAG Utrecht")
560 .setPublished(true)
561 .setAuthorizationRules(ruleAnonymousRead),
562 new GeoService()
563 .setId("ahn_terrain_model")
564 .setProtocol(QUANTIZEDMESH)
565 .setUrl(
566 "https://api.pdok.nl/kadaster/3d-basisvoorziening/ogc/v1/collections/digitaalterreinmodel")
567 .setTitle("AHN Terrain Model")
568 .setPublished(true)
569 .setAuthorizationRules(ruleAnonymousRead),
570 new GeoService()
571 .setId("3d_basisvoorziening_gebouwen_proxy")
572 .setProtocol(TILES3D)
573 .setUrl(
574 "https://api.pdok.nl/kadaster/3d-basisvoorziening/ogc/v1_0/collections/gebouwen/3dtiles")
575 .setTitle("3D Basisvoorziening Gebouwen Proxy")
576 .setPublished(true)
577 .setAuthorizationRules(ruleAnonymousRead)
578 .setSettings(new GeoServiceSettings().useProxy(true)),
579 new GeoService()
580 .setId("3d_utrecht_proxied_auth")
581 .setProtocol(TILES3D)
582 .setUrl("https://snapshot.tailormap.nl/tiles3d-proxy/3dtiles")
583 .setTitle("3D Utrecht Proxied with Authorization")
584 .setPublished(true)
585 .setAuthorizationRules(ruleAnonymousRead)
586 .setAuthentication(new ServiceAuthentication()
587 .method(ServiceAuthentication.MethodEnum.PASSWORD)
588 .username(tiles3dProxyUsername)
589 .password(tiles3dProxyPassword))
590 .setSettings(new GeoServiceSettings().useProxy(true))
591
592 );
593
594 if (map5url != null) {
595 GeoServiceLayerSettings osmAttr = new GeoServiceLayerSettings().attribution(osmAttribution);
596 GeoServiceLayerSettings map5Attr = new GeoServiceLayerSettings()
597 .attribution("Kaarten: [Map5.nl](https://map5.nl), data: " + osmAttribution);
598 services = new ArrayList<>(services);
599 services.add(new GeoService()
600 .setId("map5")
601 .setProtocol(WMTS)
602 .setTitle("Map5")
603 .setUrl(map5url)
604 .setAuthorizationRules(ruleAnonymousRead)
605 .setSettings(new GeoServiceSettings()
606 .defaultLayerSettings(new GeoServiceDefaultLayerSettings().hiDpiDisabled(true))
607 .layerSettings(Map.of(
608 "openlufo",
609 new GeoServiceLayerSettings()
610 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl), "
611 + osmAttribution),
612 "luforoadslabels",
613 osmAttr,
614 "map5topo",
615 new GeoServiceLayerSettings()
616 .attribution(map5Attr.getAttribution())
617 .hiDpiDisabled(false)
618 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
619 .hiDpiSubstituteLayer("map5topo_hq"),
620 "map5topo_gray",
621 map5Attr,
622 "map5topo_simple",
623 map5Attr,
624 "map5topo_simple_gray",
625 map5Attr,
626 "opensimpletopo",
627 osmAttr,
628 "opensimpletopo_gray",
629 osmAttr,
630 "opentopo",
631 osmAttr,
632 "opentopo_gray",
633 osmAttr))));
634 }
635
636 for (GeoService geoService : services) {
637 try {
638 geoServiceHelper.loadServiceCapabilities(geoService);
639 } catch (Exception e) {
640 logger.error(
641 "Error loading capabilities for {} service URL {}: {}, {}",
642 geoService.getProtocol().getValue(),
643 geoService.getUrl(),
644 e.getClass(),
645 e.getMessage());
646 }
647
648 geoServiceRepository.save(geoService);
649 catalogNode.addItemsItem(new TailormapObjectRef()
650 .kind(TailormapObjectRef.KindEnum.GEO_SERVICE)
651 .id(geoService.getId()));
652 }
653
654 CatalogNode wfsFeatureSourceCatalogNode =
655 new CatalogNode().id("wfs_feature_sources").title("WFS feature sources");
656 rootCatalogNode.addChildrenItem(wfsFeatureSourceCatalogNode.getId());
657 catalog.getNodes().add(wfsFeatureSourceCatalogNode);
658
659 services.stream().filter(s -> s.getProtocol() == WMS).forEach(s -> {
660 geoServiceHelper.findAndSaveRelatedWFS(s);
661 List<TMFeatureSource> linkedSources = featureSourceRepository.findByLinkedServiceId(s.getId());
662 for (TMFeatureSource linkedSource : linkedSources) {
663 wfsFeatureSourceCatalogNode.addItemsItem(new TailormapObjectRef()
664 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
665 .id(linkedSource.getId().toString()));
666 }
667 });
668
669 String geodataPassword = "980f1c8A-25933b2";
670
671 Map<String, TMFeatureSource> featureSources = Map.of(
672 "postgis",
673 new TMFeatureSource()
674 .setProtocol(TMFeatureSource.Protocol.JDBC)
675 .setTitle("PostGIS")
676 .setJdbcConnection(new JDBCConnectionProperties()
677 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
678 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
679 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
680 .database("geodata")
681 .schema("public")
682 .additionalProperties(Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
683 .setAuthentication(new ServiceAuthentication()
684 .method(ServiceAuthentication.MethodEnum.PASSWORD)
685 .username("geodata")
686 .password(geodataPassword)),
687 "postgis_osm",
688 new TMFeatureSource()
689 .setProtocol(TMFeatureSource.Protocol.JDBC)
690 .setTitle("PostGIS OSM")
691 .setJdbcConnection(new JDBCConnectionProperties()
692 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
693 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
694 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
695 .database("geodata")
696 .schema("osm")
697 .additionalProperties(Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
698 .setAuthentication(new ServiceAuthentication()
699 .method(ServiceAuthentication.MethodEnum.PASSWORD)
700 .username("geodata")
701 .password(geodataPassword)),
702 "oracle",
703 new TMFeatureSource()
704 .setProtocol(TMFeatureSource.Protocol.JDBC)
705 .setTitle("Oracle")
706 .setJdbcConnection(new JDBCConnectionProperties()
707 .dbtype(JDBCConnectionProperties.DbtypeEnum.ORACLE)
708 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "oracle")
709 .database("/FREEPDB1")
710 .schema("GEODATA")
711 .additionalProperties(Map.of("connectionOptions", "?oracle.jdbc.J2EE13Compliant=true")))
712 .setAuthentication(new ServiceAuthentication()
713 .method(ServiceAuthentication.MethodEnum.PASSWORD)
714 .username("geodata")
715 .password(geodataPassword)),
716 "sqlserver",
717 new TMFeatureSource()
718 .setProtocol(TMFeatureSource.Protocol.JDBC)
719 .setTitle("MS SQL Server")
720 .setJdbcConnection(new JDBCConnectionProperties()
721 .dbtype(JDBCConnectionProperties.DbtypeEnum.SQLSERVER)
722 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "sqlserver")
723 .database("geodata")
724 .schema("dbo")
725 .additionalProperties(Map.of("connectionOptions", ";encrypt=false")))
726 .setAuthentication(new ServiceAuthentication()
727 .method(ServiceAuthentication.MethodEnum.PASSWORD)
728 .username("geodata")
729 .password(geodataPassword)),
730 "pdok-kadaster-bestuurlijkegebieden",
731 new TMFeatureSource()
732 .setProtocol(TMFeatureSource.Protocol.WFS)
733 .setUrl(
734 "https://service.pdok.nl/kadaster/bestuurlijkegebieden/wfs/v1_0?service=WFS&VERSION=2.0.0")
735 .setTitle("Bestuurlijke gebieden")
736 .setNotes(
737 "Overzicht van de bestuurlijke indeling van Nederland in gemeenten en provincies alsmede de rijksgrens. Gegevens zijn afgeleid uit de Basisregistratie Kadaster (BRK)."));
738 featureSourceRepository.saveAll(featureSources.values());
739
740 new WFSFeatureSourceHelper().loadCapabilities(featureSources.get("pdok-kadaster-bestuurlijkegebieden"));
741 geoServiceRepository.findById("pdok-kadaster-bestuurlijkegebieden").ifPresent(geoService -> {
742 geoService
743 .getSettings()
744 .getLayerSettings()
745 .put(
746 "Provinciegebied",
747 new GeoServiceLayerSettings()
748 .description("The administrative boundary of Dutch Provinces, connected to a WFS.")
749 .featureType(new FeatureTypeRef()
750 .featureSourceId(featureSources
751 .get("pdok-kadaster-bestuurlijkegebieden")
752 .getId())
753 .featureTypeName("bestuurlijkegebieden:Provinciegebied"))
754 .title("Provinciegebied (WFS)"));
755 geoServiceRepository.save(geoService);
756 });
757
758 geoServiceRepository.findById("bestuurlijkegebieden-proxied").ifPresent(geoService -> {
759 geoService
760 .getSettings()
761 .getLayerSettings()
762 .put(
763 "Provinciegebied",
764 new GeoServiceLayerSettings()
765 .featureType(new FeatureTypeRef()
766 .featureSourceId(featureSources
767 .get("pdok-kadaster-bestuurlijkegebieden")
768 .getId())
769 .featureTypeName("bestuurlijkegebieden:Provinciegebied"))
770 .title("Provinciegebied (WFS, proxied met auth)"));
771 geoServiceRepository.save(geoService);
772 });
773
774 CatalogNode featureSourceCatalogNode =
775 new CatalogNode().id("feature_sources").title("Test feature sources");
776 rootCatalogNode.addChildrenItem(featureSourceCatalogNode.getId());
777 catalog.getNodes().add(featureSourceCatalogNode);
778
779 for (TMFeatureSource featureSource : featureSources.values()) {
780 featureSourceCatalogNode.addItemsItem(new TailormapObjectRef()
781 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
782 .id(featureSource.getId().toString()));
783 }
784 catalogRepository.save(catalog);
785
786 if (connectToSpatialDbs) {
787 featureSources.values().forEach(fs -> {
788 try {
789 if (fs.getProtocol() == TMFeatureSource.Protocol.JDBC) {
790 new JDBCFeatureSourceHelper().loadCapabilities(fs);
791 } else if (fs.getProtocol() == TMFeatureSource.Protocol.WFS) {
792 new WFSFeatureSourceHelper().loadCapabilities(fs);
793 }
794 } catch (Exception e) {
795 logger.error("Error loading capabilities for feature source {}", fs.getTitle(), e);
796 }
797 });
798
799 services.stream()
800
801
802 .filter(s -> s.getId().startsWith("snapshot-geoserver"))
803 .forEach(s -> s.getSettings()
804 .layerSettings(Map.of(
805 "postgis:begroeidterreindeel",
806 new GeoServiceLayerSettings()
807 .description(
808 """
809 This layer shows data from https:
810
811 https:
812 .featureType(new FeatureTypeRef()
813 .featureSourceId(featureSources
814 .get("postgis")
815 .getId())
816 .featureTypeName("begroeidterreindeel")),
817 "postgis:bak",
818 new GeoServiceLayerSettings()
819 .featureType(new FeatureTypeRef()
820 .featureSourceId(featureSources
821 .get("postgis")
822 .getId())
823 .featureTypeName("bak")),
824 "postgis:kadastraal_perceel",
825 new GeoServiceLayerSettings()
826 .description("cadastral parcel label points")
827 .featureType(new FeatureTypeRef()
828 .featureSourceId(featureSources
829 .get("postgis")
830 .getId())
831 .featureTypeName("kadastraal_perceel")),
832 "sqlserver:wegdeel",
833 new GeoServiceLayerSettings()
834 .attribution(
835 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)")
836 .description(
837 """
838 This layer shows data from [MS SQL Server](https:
839
840 https:
841 .featureType(new FeatureTypeRef()
842 .featureSourceId(featureSources
843 .get("sqlserver")
844 .getId())
845 .featureTypeName("wegdeel")),
846 "oracle:WATERDEEL",
847 new GeoServiceLayerSettings()
848 .description("This layer shows data from Oracle Spatial.")
849 .featureType(new FeatureTypeRef()
850 .featureSourceId(featureSources
851 .get("oracle")
852 .getId())
853 .featureTypeName("WATERDEEL")),
854 "postgis:osm_polygon",
855 new GeoServiceLayerSettings()
856 .description("This layer shows OSM data from postgis.")
857 .featureType(new FeatureTypeRef()
858 .featureSourceId(featureSources
859 .get("postgis_osm")
860 .getId())
861 .featureTypeName("osm_polygon")))));
862 }
863
864 featureSources.get("pdok-kadaster-bestuurlijkegebieden").getFeatureTypes().stream()
865 .filter(ft -> ft.getName().equals("bestuurlijkegebieden:Provinciegebied"))
866 .findFirst()
867 .ifPresent(ft -> {
868 ft.getSettings().addHideAttributesItem("identificatie");
869 ft.getSettings().addHideAttributesItem("ligtInLandCode");
870 ft.getSettings().addHideAttributesItem("fuuid");
871 ft.getSettings().putAttributeSettingsItem("naam", new AttributeSettings().title("Naam"));
872 ft.getSettings()
873 .setTemplate(
874 new FeatureTypeTemplate()
875 .templateLanguage("simple")
876 .markupLanguage("markdown")
877 .template(
878 """
879 ### Provincie
880 Deze provincie heet **{{naam}}** en ligt in _{{ligtInLandNaam}}_.
881
882 | Attribuut | Waarde |
883 | --------- | ------------------ |
884 | `code` | {{code}} |
885 | `naam` | {{naam}} |
886 | `ligt in` | {{ligtInLandNaam}} |"""));
887 });
888
889 featureSources.get("postgis").getFeatureTypes().stream()
890 .filter(ft -> ft.getName().equals("begroeidterreindeel"))
891 .findFirst()
892 .ifPresent(ft -> {
893 ft.getSettings().addHideAttributesItem("terminationdate");
894 ft.getSettings().addHideAttributesItem("geom_kruinlijn");
895 ft.getSettings().putAttributeSettingsItem("gmlid", new AttributeSettings().title("GML ID"));
896 ft.getSettings()
897 .putAttributeSettingsItem("identificatie", new AttributeSettings().title("Identificatie"));
898 ft.getSettings()
899 .putAttributeSettingsItem(
900 "tijdstipregistratie", new AttributeSettings().title("Registratie"));
901 ft.getSettings()
902 .putAttributeSettingsItem(
903 "eindregistratie", new AttributeSettings().title("Eind registratie"));
904 ft.getSettings().putAttributeSettingsItem("class", new AttributeSettings().title("Klasse"));
905 ft.getSettings()
906 .putAttributeSettingsItem("bronhouder", new AttributeSettings().title("Bronhouder"));
907 ft.getSettings()
908 .putAttributeSettingsItem("inonderzoek", new AttributeSettings().title("In onderzoek"));
909 ft.getSettings()
910 .putAttributeSettingsItem(
911 "relatievehoogteligging", new AttributeSettings().title("Relatieve hoogteligging"));
912 ft.getSettings()
913 .putAttributeSettingsItem("bgt_status", new AttributeSettings().title("BGT status"));
914 ft.getSettings()
915 .putAttributeSettingsItem("plus_status", new AttributeSettings().title("Plus-status"));
916 ft.getSettings()
917 .putAttributeSettingsItem(
918 "plus_fysiekvoorkomen", new AttributeSettings().title("Plus-fysiek voorkomen"));
919 ft.getSettings()
920 .putAttributeSettingsItem(
921 "begroeidterreindeeloptalud", new AttributeSettings().title("Op talud"));
922 ft.getSettings().addAttributeOrderItem("identificatie");
923 ft.getSettings().addAttributeOrderItem("bronhouder");
924 ft.getSettings().addAttributeOrderItem("class");
925 ft.getSettings()
926 .addAttachmentAttributesItem(new AttachmentAttributeType()
927 .attributeName("bijlage")
928 .maxAttachmentSize(4_000_000L)
929 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
930 try {
931 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
932 } catch (IOException | SQLException e) {
933 throw new RuntimeException("Failed to create attachments table", e);
934 }
935 ft.getSettings().addEditableAttributesItem("identificatie");
936 ft.getSettings().addEditableAttributesItem("bronhouder");
937 ft.getSettings().addEditableAttributesItem("class");
938 ft.getSettings().addEditableAttributesItem("gmlid");
939 ft.getSettings().addEditableAttributesItem("lv_publicatiedatum");
940 ft.getSettings().addEditableAttributesItem("creationdate");
941 ft.getSettings().addEditableAttributesItem("tijdstipregistratie");
942 ft.getSettings().addEditableAttributesItem("eindregistratie");
943 ft.getSettings().addEditableAttributesItem("terminationdate");
944 ft.getSettings().addEditableAttributesItem("inonderzoek");
945 ft.getSettings().addEditableAttributesItem("relatievehoogteligging");
946 ft.getSettings().addEditableAttributesItem("bgt_status");
947 ft.getSettings().addEditableAttributesItem("plus_status");
948 ft.getSettings().addEditableAttributesItem("plus_fysiekvoorkomen");
949 ft.getSettings().addEditableAttributesItem("begroeidterreindeeloptalud");
950 });
951
952 featureSources.get("postgis").getFeatureTypes().stream()
953 .filter(ft -> ft.getName().equals("bak"))
954 .findFirst()
955 .ifPresent(ft -> {
956 ft.getSettings().addHideAttributesItem("gmlid");
957 ft.getSettings().addHideAttributesItem("lv_publicatiedatum");
958 ft.getSettings().addHideAttributesItem("creationdate");
959 ft.getSettings().addHideAttributesItem("tijdstipregistratie");
960 ft.getSettings().addHideAttributesItem("eindregistratie");
961 ft.getSettings().addHideAttributesItem("terminationdate");
962 ft.getSettings().addHideAttributesItem("inonderzoek");
963 ft.getSettings().addHideAttributesItem("relatievehoogteligging");
964 ft.getSettings().addHideAttributesItem("bgt_status");
965 ft.getSettings().addHideAttributesItem("plus_status");
966 ft.getSettings().addHideAttributesItem("function_");
967 ft.getSettings().addHideAttributesItem("plus_type");
968 });
969
970 featureSources.get("oracle").getFeatureTypes().stream()
971 .filter(ft -> ft.getName().equals("WATERDEEL"))
972 .findFirst()
973 .ifPresent(ft -> {
974 ft.getSettings()
975 .addAttachmentAttributesItem(new AttachmentAttributeType()
976 .attributeName("bijlage")
977 .maxAttachmentSize(4_000_000L)
978 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
979 try {
980 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
981 } catch (IOException | SQLException e) {
982 throw new RuntimeException("Failed to create attachments table", e);
983 }
984 ft.getSettings().addEditableAttributesItem("GMLID");
985 ft.getSettings().addEditableAttributesItem("IDENTIFICATIE");
986 ft.getSettings().addEditableAttributesItem("LV_PUBLICATIEDATUM");
987 ft.getSettings().addEditableAttributesItem("CREATIONDATE");
988 ft.getSettings().addEditableAttributesItem("TIJDSTIPREGISTRATIE");
989 ft.getSettings().addEditableAttributesItem("EINDREGISTRATIE");
990 ft.getSettings().addEditableAttributesItem("TERMINATIONDATE");
991 ft.getSettings().addEditableAttributesItem("BRONHOUDER");
992 ft.getSettings().addEditableAttributesItem("INONDERZOEK");
993 ft.getSettings().addEditableAttributesItem("RELATIEVEHOOGTELIGGING");
994 ft.getSettings().addEditableAttributesItem("BGT_STATUS");
995 ft.getSettings().addEditableAttributesItem("PLUS_STATUS");
996 ft.getSettings().addEditableAttributesItem("CLASS");
997 ft.getSettings().addEditableAttributesItem("PLUS_TYPE");
998 });
999 featureSources.get("sqlserver").getFeatureTypes().stream()
1000 .filter(ft -> ft.getName().equals("wegdeel"))
1001 .findFirst()
1002 .ifPresent(ft -> {
1003 ft.getSettings()
1004 .addAttachmentAttributesItem(new AttachmentAttributeType()
1005 .attributeName("bijlage")
1006 .maxAttachmentSize(4_000_000L)
1007 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
1008 try {
1009 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
1010 } catch (IOException | SQLException e) {
1011 throw new RuntimeException("Failed to create attachments table", e);
1012 }
1013 ft.getSettings().addEditableAttributesItem("gmlid");
1014 ft.getSettings().addEditableAttributesItem("identificatie");
1015 ft.getSettings().addEditableAttributesItem("lv_publicatiedatum");
1016 ft.getSettings().addEditableAttributesItem("creationdate");
1017 ft.getSettings().addEditableAttributesItem("tijdstipregistratie");
1018 ft.getSettings().addEditableAttributesItem("eindregistratie");
1019 ft.getSettings().addEditableAttributesItem("terminationdate");
1020 ft.getSettings().addEditableAttributesItem("bronhouder");
1021 ft.getSettings().addEditableAttributesItem("inonderzoek");
1022 ft.getSettings().addEditableAttributesItem("relatievehoogteligging");
1023 ft.getSettings().addEditableAttributesItem("bgt_status");
1024 ft.getSettings().addEditableAttributesItem("plus_status");
1025 ft.getSettings().addEditableAttributesItem("function_");
1026 ft.getSettings().addEditableAttributesItem("plus_functiewegdeel");
1027 ft.getSettings().addEditableAttributesItem("plus_fysiekvoorkomenwegdeel");
1028 ft.getSettings().addEditableAttributesItem("surfacematerial");
1029 ft.getSettings().addEditableAttributesItem("wegdeeloptalud");
1030 });
1031
1032 featureSources.get("postgis").getFeatureTypes().stream()
1033 .filter(ft -> ft.getName().equals("kadastraal_perceel"))
1034 .findFirst()
1035 .ifPresent(ft -> {
1036 ft.getSettings()
1037 .addAttachmentAttributesItem(new AttachmentAttributeType()
1038 .attributeName("bijlage")
1039 .maxAttachmentSize(4_000_000L)
1040 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
1041 try {
1042 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
1043 } catch (IOException | SQLException e) {
1044 throw new RuntimeException("Failed to create attachments table for kadastraal_perceel", e);
1045 }
1046
1047 ft.getSettings().addHideAttributesItem("gml_id");
1048
1049 ft.getSettings().addEditableAttributesItem("begin_geldigheid");
1050 ft.getSettings().addEditableAttributesItem("tijdstip_registratie");
1051 });
1052
1053 featureSources.get("postgis_osm").getFeatureTypes().stream()
1054 .filter(ft -> ft.getName().equals("osm_polygon"))
1055 .findFirst()
1056 .ifPresent(ft -> {
1057 ft.getSettings().addEditableAttributesItem("osm_id");
1058 ft.getSettings().addEditableAttributesItem("building");
1059 ft.getSettings().addEditableAttributesItem("z_order");
1060 });
1061 }
1062
1063 public void createAppTestData() throws Exception {
1064 Upload logo = new Upload()
1065 .setCategory(Upload.CATEGORY_APP_LOGO)
1066 .setFilename("gradient.svg")
1067 .setMimeType("image/svg+xml")
1068 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
1069 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
1070 uploadRepository.save(logo);
1071
1072 List<AppTreeNode> baseNodes = List.of(
1073 new AppTreeLayerNode()
1074 .objectType("AppTreeLayerNode")
1075 .id("lyr:openbasiskaart:osm")
1076 .serviceId("openbasiskaart")
1077 .layerName("osm")
1078 .visible(true),
1079 new AppTreeLayerNode()
1080 .objectType("AppTreeLayerNode")
1081 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR")
1082 .serviceId("pdok-hwh-luchtfotorgb")
1083 .layerName("Actueel_orthoHR")
1084 .visible(false));
1085
1086 Application app = new Application()
1087 .setName("default")
1088 .setTitle("Tailormap demo")
1089 .setCrs("EPSG:28992")
1090 .setAuthorizationRules(ruleAnonymousRead)
1091 .setComponents(List.of(
1092 new Component()
1093 .type("SIMPLE_SEARCH")
1094 .config(new ComponentConfig()
1095 .enabled(true)
1096 .putAdditionalProperty("municipalities", List.of("0344"))),
1097 new Component().type("EDIT").config(new ComponentConfig().enabled(true)),
1098 new Component()
1099 .type("TOC")
1100 .config(new ComponentConfig()
1101 .enabled(true)
1102 .putAdditionalProperty("showEditLayerIcon", true)),
1103 new Component()
1104 .type("COORDINATE_LINK_WINDOW")
1105 .config(new ComponentConfig()
1106 .enabled(true)
1107 .putAdditionalProperty(
1108 "urls",
1109 List.of(
1110 Map.of(
1111 "id",
1112 "google-maps",
1113 "url",
1114 "https://www.google.com/maps/@[lat],[lon],18z",
1115 "alias",
1116 "Google Maps",
1117 "projection",
1118 "EPSG:4326"),
1119 Map.of(
1120 "id",
1121 "tm-demo",
1122 "url",
1123 "https://demo.tailormap.com/#@[X],[Y],18",
1124 "alias",
1125 "Tailormap demo",
1126 "projection",
1127 "EPSG:28992"))))))
1128 .setContentRoot(new AppContent()
1129 .addBaseLayerNodesItem(new AppTreeLevelNode()
1130 .objectType("AppTreeLevelNode")
1131 .id("root-base-layers")
1132 .root(true)
1133 .title("Base layers")
1134 .childrenIds(List.of(
1135 "lyr:openbasiskaart:osm",
1136 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1137 "lyr:openbasiskaart-proxied:osm",
1138 "lyr:openbasiskaart-tms:xyz",
1139 "lyr:b3p-mapproxy-luchtfoto:xyz")))
1140 .addBaseLayerNodesItem(
1141
1142
1143 new AppTreeLayerNode()
1144 .objectType("AppTreeLayerNode")
1145 .id("lyr:openbasiskaart-proxied:osm")
1146 .serviceId("openbasiskaart-proxied")
1147 .layerName("osm")
1148 .visible(false))
1149 .addBaseLayerNodesItem(new AppTreeLayerNode()
1150 .objectType("AppTreeLayerNode")
1151 .id("lyr:openbasiskaart-tms:xyz")
1152 .serviceId("openbasiskaart-tms")
1153 .layerName("xyz")
1154 .visible(false))
1155 .addBaseLayerNodesItem(new AppTreeLayerNode()
1156 .objectType("AppTreeLayerNode")
1157 .id("lyr:b3p-mapproxy-luchtfoto:xyz")
1158 .serviceId("b3p-mapproxy-luchtfoto")
1159 .layerName("xyz")
1160 .visible(false))
1161 .addLayerNodesItem(new AppTreeLevelNode()
1162 .objectType("AppTreeLevelNode")
1163 .id("root")
1164 .root(true)
1165 .title("Layers")
1166 .childrenIds(List.of(
1167 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1168 "lyr:bestuurlijkegebieden-proxied:Provinciegebied",
1169 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1170 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1171 "lyr:snapshot-geoserver:postgis:bak",
1172 "lyr:snapshot-geoserver:postgis:kadastraal_perceel",
1173 "lyr:snapshot-geoserver:sqlserver:wegdeel",
1174 "lyr:snapshot-geoserver:oracle:WATERDEEL",
1175 "lyr:snapshot-geoserver:BGT",
1176 "lvl:proxied",
1177 "lvl:osm",
1178 "lvl:archeo")))
1179 .addLayerNodesItem(new AppTreeLayerNode()
1180 .objectType("AppTreeLayerNode")
1181 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1182 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1183 .layerName("Provinciegebied")
1184 .visible(true))
1185
1186
1187
1188 .addLayerNodesItem(new AppTreeLayerNode()
1189 .objectType("AppTreeLayerNode")
1190 .id("lyr:bestuurlijkegebieden-proxied:Provinciegebied")
1191 .serviceId("bestuurlijkegebieden-proxied")
1192 .layerName("Provinciegebied")
1193 .visible(false))
1194 .addLayerNodesItem(new AppTreeLayerNode()
1195 .objectType("AppTreeLayerNode")
1196 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1197 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1198 .layerName("Gemeentegebied")
1199 .visible(true))
1200 .addLayerNodesItem(new AppTreeLayerNode()
1201 .objectType("AppTreeLayerNode")
1202 .id("lyr:snapshot-geoserver:postgis:begroeidterreindeel")
1203 .serviceId("snapshot-geoserver")
1204 .layerName("postgis:begroeidterreindeel")
1205 .visible(true))
1206 .addLayerNodesItem(new AppTreeLayerNode()
1207 .objectType("AppTreeLayerNode")
1208 .id("lyr:snapshot-geoserver:postgis:bak")
1209 .serviceId("snapshot-geoserver")
1210 .layerName("postgis:bak")
1211 .visible(false))
1212 .addLayerNodesItem(new AppTreeLayerNode()
1213 .objectType("AppTreeLayerNode")
1214 .id("lyr:snapshot-geoserver:postgis:kadastraal_perceel")
1215 .serviceId("snapshot-geoserver")
1216 .layerName("postgis:kadastraal_perceel")
1217 .visible(false))
1218 .addLayerNodesItem(new AppTreeLayerNode()
1219 .objectType("AppTreeLayerNode")
1220 .id("lyr:snapshot-geoserver:sqlserver:wegdeel")
1221 .serviceId("snapshot-geoserver")
1222 .layerName("sqlserver:wegdeel")
1223 .visible(true))
1224 .addLayerNodesItem(new AppTreeLayerNode()
1225 .objectType("AppTreeLayerNode")
1226 .id("lyr:snapshot-geoserver:oracle:WATERDEEL")
1227 .serviceId("snapshot-geoserver")
1228 .layerName("oracle:WATERDEEL")
1229 .visible(true))
1230 .addLayerNodesItem(new AppTreeLayerNode()
1231 .objectType("AppTreeLayerNode")
1232 .id("lyr:snapshot-geoserver:BGT")
1233 .serviceId("snapshot-geoserver")
1234 .layerName("BGT")
1235 .visible(false))
1236 .addLayerNodesItem(new AppTreeLevelNode()
1237 .objectType("AppTreeLevelNode")
1238 .id("lvl:proxied")
1239 .title("Proxied")
1240 .childrenIds(List.of("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1241 .addLayerNodesItem(new AppTreeLayerNode()
1242 .objectType("AppTreeLayerNode")
1243 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1244 .serviceId("snapshot-geoserver-proxied")
1245 .layerName("postgis:begroeidterreindeel")
1246 .visible(false))
1247 .addLayerNodesItem(new AppTreeLevelNode()
1248 .objectType("AppTreeLevelNode")
1249 .id("lvl:osm")
1250 .title("OSM")
1251 .childrenIds(List.of("lyr:snapshot-geoserver:postgis:osm_polygon")))
1252 .addLayerNodesItem(new AppTreeLayerNode()
1253 .objectType("AppTreeLayerNode")
1254 .id("lyr:snapshot-geoserver:postgis:osm_polygon")
1255 .serviceId("snapshot-geoserver")
1256 .layerName("postgis:osm_polygon")
1257 .visible(false))
1258 .addLayerNodesItem(new AppTreeLevelNode()
1259 .objectType("AppTreeLevelNode")
1260 .id("lvl:archeo")
1261 .title("Archeology")
1262 .childrenIds(List.of("lyr:demo:geomorfologie")))
1263 .addLayerNodesItem(new AppTreeLayerNode()
1264 .objectType("AppTreeLayerNode")
1265 .id("lyr:demo:geomorfologie")
1266 .serviceId("demo")
1267 .layerName("geomorfologie")
1268 .visible(true)))
1269 .setStyling(new AppStyling().logo(logo.getId().toString()))
1270 .setSettings(new AppSettings()
1271 .putLayerSettingsItem("lyr:openbasiskaart:osm", new AppLayerSettings().title("Openbasiskaart"))
1272 .putLayerSettingsItem(
1273 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR", new AppLayerSettings().title("Luchtfoto"))
1274 .putLayerSettingsItem(
1275 "lyr:openbasiskaart-proxied:osm",
1276 new AppLayerSettings().title("Openbasiskaart (proxied)"))
1277 .putLayerSettingsItem(
1278 "lyr:snapshot-geoserver:oracle:WATERDEEL",
1279 new AppLayerSettings()
1280 .opacity(50)
1281 .title("Waterdeel overridden title")
1282 .editable(true)
1283 .description("This is the layer description from the app layer setting.")
1284 .attribution(
1285 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)"))
1286 .putLayerSettingsItem(
1287 "lyr:snapshot-geoserver:postgis:osm_polygon",
1288 new AppLayerSettings()
1289 .description("OpenStreetMap polygon data in EPSG:3857")
1290 .opacity(60)
1291 .editable(true)
1292 .title("OSM Polygon (EPSG:3857)")
1293 .attribution(
1294 "© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors"))
1295 .putLayerSettingsItem(
1296 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1297 new AppLayerSettings()
1298 .editable(true)
1299 .addHideAttributesItem("begroeidterreindeeloptalud")
1300 .addReadOnlyAttributesItem("eindregistratie"))
1301 .putLayerSettingsItem(
1302 "lyr:snapshot-geoserver:postgis:kadastraal_perceel",
1303 new AppLayerSettings().editable(true).addReadOnlyAttributesItem("aanduiding"))
1304 .putLayerSettingsItem(
1305 "lyr:snapshot-geoserver:sqlserver:wegdeel", new AppLayerSettings().editable(true))
1306 .putLayerSettingsItem(
1307 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel",
1308 new AppLayerSettings().editable(false))
1309 .putLayerSettingsItem(
1310 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1311 new AppLayerSettings()
1312 .hiddenFunctionality(Set.of(FEATURE_INFO, ATTRIBUTE_LIST, EXPORT)))
1313 .addFilterGroupsItem(new FilterGroup()
1314 .id("filtergroup1")
1315 .source("PRESET")
1316 .type(FilterGroup.TypeEnum.ATTRIBUTE)
1317 .layerIds(List.of("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1318 .operator(FilterGroup.OperatorEnum.AND)
1319 .addFiltersItem(new Filter()
1320 .id("filter1")
1321 .type(Filter.TypeEnum.ATTRIBUTE)
1322 .condition(Filter.ConditionEnum.BEFORE)
1323 .addValueItem("2025-06-05")
1324 .attribute("creationdate")
1325 .attributeType(Filter.AttributeTypeEnum.DATE))
1326 .addFiltersItem(new Filter()
1327 .id("filter2")
1328 .type(Filter.TypeEnum.ATTRIBUTE)
1329 .condition(Filter.ConditionEnum.UNIQUE_VALUES)
1330 .addValueItem("bodembedekkers")
1331 .addValueItem("bosplantsoen")
1332 .addValueItem("gras- en kruidachtigen")
1333 .attribute("plus_fysiekvoorkomen")
1334 .attributeType(Filter.AttributeTypeEnum.STRING)
1335 .editConfiguration(new FilterEditConfiguration()
1336 .filterTool(FilterEditConfiguration.FilterToolEnum.CHECKBOX)
1337 .attributeValuesSettings(List.of(
1338 new AttributeValueSettings()
1339 .value("bodembedekkers")
1340 .initiallySelected(true)
1341 .selectable(true)
1342 .alias("Bodembedekkers"),
1343 new AttributeValueSettings()
1344 .value("bosplantsoen")
1345 .initiallySelected(true)
1346 .selectable(true)
1347 .alias("Bosplantsoen"),
1348 new AttributeValueSettings()
1349 .value("gras- en kruidachtigen")
1350 .initiallySelected(true)
1351 .selectable(true)
1352 .alias("Gras- en kruidachtigen"),
1353 new AttributeValueSettings()
1354 .value("griend en hakhout")
1355 .initiallySelected(false)
1356 .selectable(true),
1357 new AttributeValueSettings()
1358 .value("heesters")
1359 .initiallySelected(false)
1360 .selectable(true),
1361 new AttributeValueSettings()
1362 .value("planten")
1363 .initiallySelected(false)
1364 .selectable(true),
1365 new AttributeValueSettings()
1366 .value("struikrozen")
1367 .initiallySelected(false)
1368 .selectable(true),
1369 new AttributeValueSettings()
1370 .value("waardeOnbekend")
1371 .initiallySelected(false)
1372 .selectable(true))))))
1373 .addFilterGroupsItem(new FilterGroup()
1374 .id("filtergroup2")
1375 .source("PRESET")
1376 .type(FilterGroup.TypeEnum.ATTRIBUTE)
1377 .layerIds(List.of("lyr:snapshot-geoserver:postgis:kadastraal_perceel"))
1378 .operator(FilterGroup.OperatorEnum.AND)
1379 .addFiltersItem(new Filter()
1380 .id("filter3")
1381 .type(Filter.TypeEnum.ATTRIBUTE)
1382 .condition(Filter.ConditionEnum.u)
1383 .addValueItem("1")
1384 .addValueItem("12419")
1385 .attribute("perceelnummer")
1386 .attributeType(Filter.AttributeTypeEnum.DOUBLE)
1387 .editConfiguration(new FilterEditConfiguration()
1388 .filterTool(FilterEditConfiguration.FilterToolEnum.SLIDER)
1389 .initialLowerValue(1d)
1390 .initialUpperValue(12419d)
1391 .minimumValue(1d)
1392 .maximumValue(12419d)))));
1393
1394 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1395 app.setInitialExtent(
1396 new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d));
1397 app.setMaxExtent(new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d));
1398
1399 if (map5url != null) {
1400 AppTreeLevelNode root =
1401 (AppTreeLevelNode) app.getContentRoot().getBaseLayerNodes().getFirst();
1402 List<String> childrenIds = new ArrayList<>(root.getChildrenIds());
1403 childrenIds.add("lyr:map5:map5topo");
1404 childrenIds.add("lyr:map5:map5topo_simple");
1405 childrenIds.add("lvl:luchtfoto-labels");
1406 root.setChildrenIds(childrenIds);
1407 app.getSettings()
1408 .putLayerSettingsItem("lyr:map5:map5topo", new AppLayerSettings().title("Map5"))
1409 .putLayerSettingsItem("lyr:map5:map5topo_simple", new AppLayerSettings().title("Map5 simple"));
1410 app.getContentRoot()
1411 .addBaseLayerNodesItem(new AppTreeLayerNode()
1412 .objectType("AppTreeLayerNode")
1413 .id("lyr:map5:map5topo")
1414 .serviceId("map5")
1415 .layerName("map5topo")
1416 .visible(false))
1417 .addBaseLayerNodesItem(new AppTreeLayerNode()
1418 .objectType("AppTreeLayerNode")
1419 .id("lyr:map5:map5topo_simple")
1420 .serviceId("map5")
1421 .layerName("map5topo_simple")
1422 .visible(false))
1423 .addBaseLayerNodesItem(new AppTreeLevelNode()
1424 .objectType("AppTreeLevelNode")
1425 .id("lvl:luchtfoto-labels")
1426 .title("Luchtfoto met labels")
1427 .addChildrenIdsItem("lyr:map5:luforoadslabels")
1428 .addChildrenIdsItem("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2"))
1429 .addBaseLayerNodesItem(new AppTreeLayerNode()
1430 .objectType("AppTreeLayerNode")
1431 .id("lyr:map5:luforoadslabels")
1432 .serviceId("map5")
1433 .layerName("luforoadslabels")
1434 .visible(false))
1435 .addBaseLayerNodesItem(new AppTreeLayerNode()
1436 .objectType("AppTreeLayerNode")
1437 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2")
1438 .serviceId("pdok-hwh-luchtfotorgb")
1439 .layerName("Actueel_orthoHR")
1440 .visible(false));
1441 }
1442
1443 applicationRepository.save(app);
1444
1445 app = new Application()
1446 .setName("base")
1447 .setTitle("Service base app")
1448 .setCrs("EPSG:28992")
1449 .setAuthorizationRules(ruleAnonymousRead)
1450 .setContentRoot(new AppContent()
1451 .addBaseLayerNodesItem(new AppTreeLevelNode()
1452 .objectType("AppTreeLevelNode")
1453 .id("root-base-layers")
1454 .root(true)
1455 .title("Base layers")
1456 .childrenIds(List.of(
1457 "lyr:openbasiskaart:osm", "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR"))));
1458 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1459 applicationRepository.save(app);
1460
1461 app = new Application()
1462 .setName("secured")
1463 .setTitle("secured app")
1464 .setCrs("EPSG:28992")
1465 .setAuthorizationRules(ruleLoggedIn)
1466 .setContentRoot(new AppContent()
1467 .addBaseLayerNodesItem(new AppTreeLevelNode()
1468 .objectType("AppTreeLevelNode")
1469 .id("root-base-layers")
1470 .root(true)
1471 .title("Base layers")
1472 .childrenIds(List.of(
1473 "lyr:openbasiskaart:osm",
1474 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1475 "lyr:openbasiskaart-proxied:osm")))
1476 .addBaseLayerNodesItem(new AppTreeLayerNode()
1477 .objectType("AppTreeLayerNode")
1478 .id("lyr:openbasiskaart-proxied:osm")
1479 .serviceId("openbasiskaart-proxied")
1480 .layerName("osm")
1481 .visible(false))
1482 .addLayerNodesItem(new AppTreeLevelNode()
1483 .objectType("AppTreeLevelNode")
1484 .id("root")
1485 .root(true)
1486 .title("Layers")
1487 .childrenIds(List.of(
1488 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1489 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1490 "lvl:proxied")))
1491 .addLayerNodesItem(new AppTreeLayerNode()
1492 .objectType("AppTreeLayerNode")
1493 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1494 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1495 .layerName("Gemeentegebied")
1496 .visible(true))
1497 .addLayerNodesItem(new AppTreeLayerNode()
1498 .objectType("AppTreeLayerNode")
1499 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1500 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1501 .layerName("Provinciegebied")
1502 .visible(false))
1503 .addLayerNodesItem(new AppTreeLevelNode()
1504 .objectType("AppTreeLevelNode")
1505 .id("lvl:proxied")
1506 .title("Proxied")
1507 .childrenIds(List.of("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1508 .addLayerNodesItem(new AppTreeLayerNode()
1509 .objectType("AppTreeLayerNode")
1510 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1511 .serviceId("snapshot-geoserver-proxied")
1512 .layerName("postgis:begroeidterreindeel")
1513 .visible(false)))
1514 .setSettings(new AppSettings()
1515 .putLayerSettingsItem(
1516 "lyr:openbasiskaart-proxied:osm",
1517 new AppLayerSettings().title("Openbasiskaart (proxied)")));
1518
1519 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1520 applicationRepository.save(app);
1521
1522 app = new Application()
1523 .setName("secured-auth")
1524 .setTitle("secured (with authorizations)")
1525 .setCrs("EPSG:28992")
1526 .setAuthorizationRules(List.of(
1527 new AuthorizationRule()
1528 .groupName("test-foo")
1529 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)),
1530 new AuthorizationRule()
1531 .groupName("test-bar")
1532 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))
1533 .setContentRoot(new AppContent()
1534 .addLayerNodesItem(new AppTreeLevelNode()
1535 .objectType("AppTreeLevelNode")
1536 .id("root")
1537 .root(true)
1538 .title("Layers")
1539 .childrenIds(List.of("lvl:needs-auth", "lvl:public")))
1540 .addLayerNodesItem(new AppTreeLevelNode()
1541 .objectType("AppTreeLevelNode")
1542 .id("lvl:public")
1543 .title("Public")
1544 .childrenIds(List.of("lyr:snapshot-geoserver:BGT")))
1545 .addLayerNodesItem(new AppTreeLevelNode()
1546 .objectType("AppTreeLevelNode")
1547 .id("lvl:needs-auth")
1548 .title("Needs auth")
1549 .childrenIds(List.of(
1550 "lyr:filtered-snapshot-geoserver:BGT",
1551 "lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1552 .addLayerNodesItem(new AppTreeLayerNode()
1553 .objectType("AppTreeLayerNode")
1554 .id("lyr:filtered-snapshot-geoserver:BGT")
1555 .serviceId("filtered-snapshot-geoserver")
1556 .layerName("BGT")
1557 .visible(true))
1558 .addLayerNodesItem(new AppTreeLayerNode()
1559 .objectType("AppTreeLayerNode")
1560 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1561 .serviceId("filtered-snapshot-geoserver")
1562 .layerName("postgis:begroeidterreindeel")
1563 .visible(true))
1564 .addLayerNodesItem(new AppTreeLayerNode()
1565 .objectType("AppTreeLayerNode")
1566 .id("lyr:snapshot-geoserver:BGT")
1567 .serviceId("snapshot-geoserver")
1568 .layerName("BGT")
1569 .visible(true)));
1570
1571 applicationRepository.save(app);
1572
1573 app = new Application()
1574 .setName("austria")
1575 .setCrs("EPSG:3857")
1576 .setAuthorizationRules(ruleAnonymousRead)
1577 .setTitle("Austria")
1578 .setInitialExtent(
1579 new Bounds().minx(987982d).miny(5799551d).maxx(1963423d).maxy(6320708d))
1580 .setMaxExtent(
1581 new Bounds().minx(206516d).miny(5095461d).maxx(3146930d).maxy(7096232d))
1582 .setContentRoot(new AppContent()
1583 .addBaseLayerNodesItem(new AppTreeLevelNode()
1584 .objectType("AppTreeLevelNode")
1585 .id("root-base-layers")
1586 .root(true)
1587 .title("Base layers")
1588 .childrenIds(List.of(
1589 "lyr:at-basemap:geolandbasemap",
1590 "lyr:at-basemap:orthofoto",
1591 "lvl:orthofoto-labels",
1592 "lyr:osm:xyz")))
1593 .addBaseLayerNodesItem(new AppTreeLayerNode()
1594 .objectType("AppTreeLayerNode")
1595 .id("lyr:at-basemap:geolandbasemap")
1596 .serviceId("at-basemap")
1597 .layerName("geolandbasemap")
1598 .visible(true))
1599 .addBaseLayerNodesItem(new AppTreeLayerNode()
1600 .objectType("AppTreeLayerNode")
1601 .id("lyr:at-basemap:orthofoto")
1602 .serviceId("at-basemap")
1603 .layerName("bmaporthofoto30cm")
1604 .visible(false))
1605 .addBaseLayerNodesItem(new AppTreeLevelNode()
1606 .objectType("AppTreeLevelNode")
1607 .id("lvl:orthofoto-labels")
1608 .title("Orthophoto with labels")
1609 .childrenIds(List.of("lyr:at-basemap:bmapoverlay", "lyr:at-basemap:orthofoto_2")))
1610 .addBaseLayerNodesItem(new AppTreeLayerNode()
1611 .objectType("AppTreeLayerNode")
1612 .id("lyr:at-basemap:bmapoverlay")
1613 .serviceId("at-basemap")
1614 .layerName("bmapoverlay")
1615 .visible(false))
1616 .addBaseLayerNodesItem(new AppTreeLayerNode()
1617 .objectType("AppTreeLayerNode")
1618 .id("lyr:at-basemap:orthofoto_2")
1619 .serviceId("at-basemap")
1620 .layerName("bmaporthofoto30cm")
1621 .visible(false))
1622 .addBaseLayerNodesItem(new AppTreeLayerNode()
1623 .objectType("AppTreeLayerNode")
1624 .id("lyr:osm:xyz")
1625 .serviceId("osm")
1626 .layerName("xyz")
1627 .visible(false)));
1628
1629 applicationRepository.save(app);
1630
1631 app = new Application()
1632 .setName("3d_utrecht")
1633 .setCrs("EPSG:3857")
1634 .setAuthorizationRules(ruleLoggedIn)
1635 .setTitle("3D Utrecht")
1636 .setInitialExtent(
1637 new Bounds().minx(558390d).miny(6818485d).maxx(566751d).maxy(6824036d))
1638 .setMaxExtent(
1639 new Bounds().minx(91467d).miny(6496479d).maxx(1037043d).maxy(7147453d))
1640 .setSettings(new AppSettings().uiSettings(new AppUiSettings().enable3D(true)))
1641 .setContentRoot(new AppContent()
1642 .addBaseLayerNodesItem(new AppTreeLevelNode()
1643 .objectType("AppTreeLevelNode")
1644 .id("root-base-layers")
1645 .root(true)
1646 .title("Base layers")
1647 .childrenIds(List.of("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR", "lyr:osm:xyz")))
1648 .addBaseLayerNodesItem(new AppTreeLayerNode()
1649 .objectType("AppTreeLayerNode")
1650 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR")
1651 .serviceId("pdok-hwh-luchtfotorgb")
1652 .layerName("Actueel_orthoHR")
1653 .visible(true))
1654 .addBaseLayerNodesItem(new AppTreeLayerNode()
1655 .objectType("AppTreeLayerNode")
1656 .id("lyr:osm:xyz")
1657 .serviceId("osm")
1658 .layerName("xyz")
1659 .visible(false))
1660 .addLayerNodesItem(new AppTreeLevelNode()
1661 .objectType("AppTreeLevelNode")
1662 .id("root")
1663 .root(true)
1664 .title("Layers")
1665 .childrenIds(List.of(
1666 "lyr:3dbag_utrecht:tiles3d",
1667 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1668 "lyr:3d_basisvoorziening_gebouwen_proxy:tiles3d",
1669 "lyr:3d_utrecht_proxied_auth:tiles3d")))
1670 .addLayerNodesItem(new AppTreeLayerNode()
1671 .objectType("AppTreeLayerNode")
1672 .id("lyr:3dbag_utrecht:tiles3d")
1673 .serviceId("3dbag_utrecht")
1674 .layerName("tiles3d")
1675 .visible(true))
1676 .addLayerNodesItem(new AppTreeLayerNode()
1677 .objectType("AppTreeLayerNode")
1678 .id("lyr:3d_basisvoorziening_gebouwen_proxy:tiles3d")
1679 .serviceId("3d_basisvoorziening_gebouwen_proxy")
1680 .layerName("tiles3d")
1681 .visible(false))
1682 .addLayerNodesItem(new AppTreeLayerNode()
1683 .objectType("AppTreeLayerNode")
1684 .id("lyr:3d_utrecht_proxied_auth:tiles3d")
1685 .serviceId("3d_utrecht_proxied_auth")
1686 .layerName("tiles3d")
1687 .visible(false))
1688 .addLayerNodesItem(new AppTreeLayerNode()
1689 .objectType("AppTreeLayerNode")
1690 .id("lyr:snapshot-geoserver:postgis:begroeidterreindeel")
1691 .serviceId("snapshot-geoserver")
1692 .layerName("postgis:begroeidterreindeel")
1693 .visible(true))
1694 .addTerrainLayerNodesItem(new AppTreeLevelNode()
1695 .objectType("AppTreeLevelNode")
1696 .id("root-terrain-layers")
1697 .root(true)
1698 .title("Terrain Layers")
1699 .childrenIds(List.of("lyr:ahn_terrain_model:quantizedmesh")))
1700 .addTerrainLayerNodesItem(new AppTreeLayerNode()
1701 .objectType("AppTreeLayerNode")
1702 .id("lyr:ahn_terrain_model:quantizedmesh")
1703 .serviceId("ahn_terrain_model")
1704 .layerName("quantizedmesh")
1705 .visible(false)));
1706
1707 applicationRepository.save(app);
1708
1709 app = new Application()
1710 .setName("public-with-auth")
1711 .setTitle("Public app with one restricted layer in a group")
1712 .setCrs("EPSG:28992")
1713 .setAuthorizationRules(ruleAnonymousRead)
1714 .setInitialExtent(
1715 new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d))
1716 .setMaxExtent(
1717 new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d))
1718 .setStyling(new AppStyling().logo(logo.getId().toString()))
1719 .setContentRoot(new AppContent()
1720 .addBaseLayerNodesItem(new AppTreeLevelNode()
1721 .objectType("AppTreeLevelNode")
1722 .id("root")
1723 .root(true)
1724 .title("Basemaps")
1725 .childrenIds(List.of("lyr:openbasiskaart:osm")))
1726 .addBaseLayerNodesItem(new AppTreeLayerNode()
1727 .objectType("AppTreeLayerNode")
1728 .id("lyr:openbasiskaart:osm")
1729 .serviceId("openbasiskaart")
1730 .layerName("osm")
1731 .visible(true))
1732 .addLayerNodesItem(new AppTreeLevelNode()
1733 .objectType("AppTreeLevelNode")
1734 .id("root")
1735 .root(true)
1736 .title("Application layers")
1737 .childrenIds(List.of(
1738 "lyr:snapshot-geoserver:postgis:kadastraal_perceel", "xpfhl34VmghkU12nP9Jer")))
1739 .addLayerNodesItem(new AppTreeLayerNode()
1740 .objectType("AppTreeLayerNode")
1741 .id("lyr:snapshot-geoserver:postgis:kadastraal_perceel")
1742 .serviceId("snapshot-geoserver")
1743 .layerName("postgis:kadastraal_perceel")
1744 .visible(true))
1745 .addLayerNodesItem(new AppTreeLevelNode()
1746 .id("xpfhl34VmghkU12nP9Jer")
1747 .root(false)
1748 .title("restricted")
1749 .objectType("AppTreeLevelNode")
1750 .childrenIds(List.of("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1751 .addLayerNodesItem(new AppTreeLayerNode()
1752 .objectType("AppTreeLayerNode")
1753 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1754 .visible(true)
1755 .serviceId("filtered-snapshot-geoserver")
1756 .layerName("postgis:begroeidterreindeel")))
1757 .setSettings(new AppSettings());
1758
1759 applicationRepository.save(app);
1760
1761 Configuration config = new Configuration();
1762 config.setKey(Configuration.DEFAULT_APP);
1763 config.setValue("default");
1764 configurationRepository.save(config);
1765 config = new Configuration();
1766 config.setKey(Configuration.DEFAULT_BASE_APP);
1767 config.setValue("base");
1768 configurationRepository.save(config);
1769 }
1770
1771 private void createConfigurationTestData() throws JsonProcessingException {
1772 Configuration config = new Configuration();
1773 config.setKey("test");
1774 config.setAvailableForViewer(true);
1775 config.setValue("test value");
1776 config.setJsonValue(new ObjectMapper().readTree("{ \"someProperty\": 1, \"nestedObject\": { \"num\": 42 } }"));
1777 configurationRepository.save(config);
1778 }
1779
1780 @Transactional
1781 public void createSolrIndex() throws Exception {
1782 if (connectToSpatialDbs) {
1783
1784
1785 featureSourceRepository.flush();
1786
1787 logger.info("Creating Solr index");
1788 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
1789 final String solrUrl = "http://" + (connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "solr") + ":8983/solr/";
1790 this.solrService.setSolrUrl(solrUrl);
1791 SolrHelper solrHelper = new SolrHelper(this.solrService.getSolrClientForIndexing())
1792 .withBatchSize(solrBatchSize)
1793 .withGeometryValidationRule(solrGeometryValidationRule);
1794 GeoService geoService =
1795 geoServiceRepository.findById("snapshot-geoserver").orElseThrow();
1796 Application defaultApp = applicationRepository.findByName("default");
1797
1798 TMFeatureType begroeidterreindeelFT = geoService.findFeatureTypeForLayer(
1799 geoService.findLayer("postgis:begroeidterreindeel"), featureSourceRepository);
1800
1801 TMFeatureType wegdeelFT = geoService.findFeatureTypeForLayer(
1802 geoService.findLayer("sqlserver:wegdeel"), featureSourceRepository);
1803
1804 TMFeatureType kadastraalPerceelFT = geoService.findFeatureTypeForLayer(
1805 geoService.findLayer("postgis:kadastraal_perceel"), featureSourceRepository);
1806
1807 try (solrHelper) {
1808 SearchIndex begroeidterreindeelIndex = null;
1809 if (begroeidterreindeelFT != null) {
1810 begroeidterreindeelIndex = new SearchIndex()
1811 .setName("Begroeidterreindeel")
1812 .setFeatureTypeId(begroeidterreindeelFT.getId())
1813 .setSearchFieldsUsed(List.of("class", "plus_fysiekvoorkomen", "bronhouder"))
1814 .setSearchDisplayFieldsUsed(List.of("class", "plus_fysiekvoorkomen"));
1815 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1816 begroeidterreindeelIndex = solrHelper.addFeatureTypeIndex(
1817 begroeidterreindeelIndex,
1818 begroeidterreindeelFT,
1819 featureSourceFactoryHelper,
1820 searchIndexRepository);
1821 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1822 }
1823
1824 SearchIndex kadastraalPerceelIndex = null;
1825 if (kadastraalPerceelFT != null) {
1826 kadastraalPerceelIndex = new SearchIndex()
1827 .setName("kadastraal_perceel")
1828 .setFeatureTypeId(kadastraalPerceelFT.getId())
1829 .setSearchFieldsUsed(List.of("aanduiding"))
1830 .setSearchDisplayFieldsUsed(List.of("aanduiding"));
1831 kadastraalPerceelIndex = searchIndexRepository.save(kadastraalPerceelIndex);
1832 kadastraalPerceelIndex = solrHelper.addFeatureTypeIndex(
1833 kadastraalPerceelIndex,
1834 kadastraalPerceelFT,
1835 featureSourceFactoryHelper,
1836 searchIndexRepository);
1837 kadastraalPerceelIndex = searchIndexRepository.save(kadastraalPerceelIndex);
1838 }
1839
1840 SearchIndex wegdeelIndex = null;
1841 if (wegdeelFT != null) {
1842 wegdeelIndex = new SearchIndex()
1843 .setName("Wegdeel")
1844 .setFeatureTypeId(wegdeelFT.getId())
1845 .setSearchFieldsUsed(List.of(
1846 "function_", "plus_fysiekvoorkomenwegdeel", "surfacematerial", "bronhouder"))
1847 .setSearchDisplayFieldsUsed(List.of("function_", "plus_fysiekvoorkomenwegdeel"));
1848 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1849 wegdeelIndex = solrHelper.addFeatureTypeIndex(
1850 wegdeelIndex, wegdeelFT, featureSourceFactoryHelper, searchIndexRepository);
1851 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1852 }
1853
1854 featureSourceRepository
1855 .getByTitle("PostGIS")
1856 .flatMap(fs -> fs.getFeatureTypes().stream()
1857 .filter(ft -> ft.getName().equals("bak"))
1858 .findFirst())
1859 .ifPresent(ft -> {
1860 SearchIndex bak = new SearchIndex()
1861 .setName("bak")
1862 .setFeatureTypeId(ft.getId())
1863 .setSearchFieldsUsed(List.of("gmlid", "identificatie", "plus_type"))
1864 .setSearchDisplayFieldsUsed(List.of("gmlid", "plus_type", "bronhouder"));
1865 searchIndexRepository.save(bak);
1866 try {
1867 bak = solrHelper.addFeatureTypeIndex(
1868 bak, ft, featureSourceFactoryHelper, searchIndexRepository);
1869 searchIndexRepository.save(bak);
1870 } catch (IOException | SolrServerException e) {
1871 throw new RuntimeException(e);
1872 }
1873 });
1874
1875
1876
1877 featureSourceRepository
1878 .getByTitle("PostGIS OSM")
1879 .flatMap(fs -> fs.getFeatureTypes().stream()
1880 .filter(ft -> ft.getName().equals("osm_roads"))
1881 .findFirst())
1882 .ifPresent(ft -> {
1883 SearchIndex osm_no_pk = new SearchIndex()
1884 .setName("osm_no_pk")
1885 .setFeatureTypeId(ft.getId())
1886 .setSearchFieldsUsed(List.of("landuse", "osm_id", "natural", "boundary"))
1887 .setSearchDisplayFieldsUsed(
1888 List.of("landuse", "osm_id", "natural", "amenity", "boundary"));
1889 searchIndexRepository.save(osm_no_pk);
1890 });
1891
1892 AppTreeLayerNode begroeidTerreindeelLayerNode = defaultApp
1893 .getAllAppTreeLayerNode()
1894 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1895 .findFirst()
1896 .orElse(null);
1897
1898 if (begroeidTerreindeelLayerNode != null && begroeidterreindeelIndex != null) {
1899 defaultApp
1900 .getAppLayerSettings(begroeidTerreindeelLayerNode)
1901 .setSearchIndexId(begroeidterreindeelIndex.getId());
1902 }
1903
1904 AppTreeLayerNode kadastraalPerceelLayerNode = defaultApp
1905 .getAllAppTreeLayerNode()
1906 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:postgis:kadastraal_perceel"))
1907 .findFirst()
1908 .orElse(null);
1909
1910 if (kadastraalPerceelLayerNode != null && kadastraalPerceelIndex != null) {
1911 defaultApp
1912 .getAppLayerSettings(kadastraalPerceelLayerNode)
1913 .setSearchIndexId(kadastraalPerceelIndex.getId());
1914 }
1915
1916 AppTreeLayerNode wegdeel = defaultApp
1917 .getAllAppTreeLayerNode()
1918 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:sqlserver:wegdeel"))
1919 .findFirst()
1920 .orElse(null);
1921
1922 if (wegdeel != null && wegdeelIndex != null) {
1923 defaultApp.getAppLayerSettings(wegdeel).setSearchIndexId(wegdeelIndex.getId());
1924 }
1925
1926 applicationRepository.save(defaultApp);
1927 }
1928 }
1929 }
1930
1931 private void createSearchIndexTasks() {
1932 logger.info("Creating search index tasks");
1933 List.of("Begroeidterreindeel", "kadastraal_perceel")
1934 .forEach(name -> searchIndexRepository.findByName(name).ifPresent(index -> {
1935 index.setSchedule(new TaskSchedule()
1936
1937 .cronExpression("0 0 0/1 1/1 * ? *")
1938
1939
1940 .description("Update Solr index \"" + name + "\" every hour"));
1941 try {
1942 final UUID uuid = taskManagerService.createTask(
1943 IndexTask.class,
1944 new TMJobDataMap(Map.of(
1945 Task.TYPE_KEY,
1946 TaskType.INDEX,
1947 Task.DESCRIPTION_KEY,
1948 index.getSchedule().getDescription(),
1949 IndexTask.INDEX_KEY,
1950 index.getId().toString(),
1951 Task.PRIORITY_KEY,
1952 10)),
1953 index.getSchedule().getCronExpression());
1954
1955 index.getSchedule().setUuid(uuid);
1956 searchIndexRepository.save(index);
1957
1958 logger.info("Created task to update Solr index with key: {}", uuid);
1959 } catch (SchedulerException e) {
1960 logger.error("Error creating scheduled solr index task", e);
1961 }
1962 }));
1963 }
1964
1965 private void createPages() throws IOException {
1966 Upload logo = new Upload()
1967 .setCategory(Upload.CATEGORY_PORTAL_IMAGE)
1968 .setFilename("gradient.svg")
1969 .setMimeType("image/svg+xml")
1970 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
1971 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
1972 uploadRepository.save(logo);
1973
1974 Page loggedIn = new Page();
1975 loggedIn.setAuthorizationRules(ruleLoggedIn);
1976 loggedIn.setName("loggedIn");
1977 loggedIn.setType("page");
1978 loggedIn.setContent("About Tailormap");
1979 loggedIn.setContent("""
1980 # About Tailormap
1981 This is a page for logged in users.
1982 """);
1983 pageRepository.save(loggedIn);
1984
1985 Page about = new Page();
1986 about.setAuthorizationRules(ruleAnonymousRead);
1987 about.setName("about");
1988 about.setType("page");
1989 about.setContent("About Tailormap");
1990 about.setContent("""
1991 # About Tailormap
1992
1993 This is a page about *Tailormap*. It doesn't say much yet.
1994 """);
1995 pageRepository.save(about);
1996
1997 Page page = new Page();
1998 page.setAuthorizationRules(ruleAnonymousRead);
1999 page.setName("home");
2000 page.setType("page");
2001 page.setTitle("Tailormap - Home");
2002 page.setContent(
2003 """
2004 # Welcome to Tailormap!
2005
2006 This page is only visible when you implement a frontend to display pages, or get it (including a simple CMS)
2007 from [B3Partners](https:
2008 """);
2009 page.setClassName(null);
2010 page.setTiles(List.of(
2011 new PageTile()
2012 .authorizationRules(ruleAnonymousRead)
2013 .id(UUID.randomUUID().toString())
2014 .title("Default app")
2015 .tileType(PageTile.TileTypeEnum.APPLICATION)
2016 .applicationId(Optional.ofNullable(applicationRepository.findByName("default"))
2017 .map(Application::getId)
2018 .orElse(null))
2019 .image(logo.getId().toString())
2020 .content("*Default app* tile content")
2021 .filterRequireAuthorization(false)
2022 .openInNewWindow(false),
2023 new PageTile()
2024 .authorizationRules(ruleAnonymousRead)
2025 .id(UUID.randomUUID().toString())
2026 .title("Secured app")
2027 .tileType(PageTile.TileTypeEnum.APPLICATION)
2028 .applicationId(Optional.ofNullable(applicationRepository.findByName("secured"))
2029 .map(Application::getId)
2030 .orElse(null))
2031 .filterRequireAuthorization(true)
2032 .content("Secure app, only shown if user has authorization")
2033 .openInNewWindow(false),
2034 new PageTile()
2035 .authorizationRules(ruleAnonymousRead)
2036 .id(UUID.randomUUID().toString())
2037 .title("Secured app (unfiltered)")
2038 .tileType(PageTile.TileTypeEnum.APPLICATION)
2039 .applicationId(Optional.ofNullable(applicationRepository.findByName("secured"))
2040 .map(Application::getId)
2041 .orElse(null))
2042 .filterRequireAuthorization(false)
2043 .content("Secure app, tile shown to everyone")
2044 .openInNewWindow(false),
2045 new PageTile()
2046 .authorizationRules(ruleAnonymousRead)
2047 .id(UUID.randomUUID().toString())
2048 .title("About")
2049 .tileType(PageTile.TileTypeEnum.PAGE)
2050 .pageId(about.getId())
2051 .openInNewWindow(false),
2052 new PageTile()
2053 .authorizationRules(ruleAnonymousRead)
2054 .id(UUID.randomUUID().toString())
2055 .title("B3Partners")
2056 .tileType(PageTile.TileTypeEnum.URL)
2057 .url("https://www.b3partners.nl/")
2058 .openInNewWindow(true),
2059 new PageTile()
2060 .authorizationRules(ruleLoggedIn)
2061 .id(UUID.randomUUID().toString())
2062 .title("Github repository")
2063 .tileType(PageTile.TileTypeEnum.URL)
2064 .url("https://github.com/Tailormap/tailormap-viewer")
2065 .openInNewWindow(true),
2066 new PageTile()
2067 .authorizationRules(ruleAnonymousRead)
2068 .id(UUID.randomUUID().toString())
2069 .tileType(PageTile.TileTypeEnum.PAGE)
2070 .title("Secured page")
2071 .pageId(loggedIn.getId())
2072 .filterRequireAuthorization(true)
2073 .openInNewWindow(true),
2074 new PageTile()
2075 .authorizationRules(ruleAnonymousRead)
2076 .id(UUID.randomUUID().toString())
2077 .tileType(PageTile.TileTypeEnum.PAGE)
2078 .title("Secured page (unfiltered)")
2079 .pageId(loggedIn.getId())
2080 .filterRequireAuthorization(false)
2081 .openInNewWindow(true)));
2082 pageRepository.save(page);
2083
2084 Configuration c = new Configuration();
2085 c.setKey(HOME_PAGE);
2086 c.setValue(page.getId().toString());
2087 configurationRepository.save(c);
2088
2089 List<MenuItem> globalMenuItems = List.of(
2090 new MenuItem().pageId(about.getId()).label("About").openInNewWindow(false),
2091 new MenuItem()
2092 .label("B3Partners website")
2093 .url("https://www.b3partners.nl/")
2094 .openInNewWindow(true)
2095 .exclusiveOnPageId(about.getId()));
2096 c = new Configuration();
2097 c.setKey(PORTAL_MENU);
2098 c.setJsonValue(new ObjectMapper().valueToTree(globalMenuItems));
2099 configurationRepository.save(c);
2100 }
2101
2102 private void insertTestDrawing() {
2103
2104 try {
2105 this.jdbcClient
2106 .sql(
2107 """
2108 INSERT INTO data.drawing (id,name,description,domain_data,"access",created_by,created_at,updated_by,updated_at,srid,"version") VALUES
2109 ('38faa008-013e-49d4-9528-8f58c94d8791'::uuid,'Testcase','A private access drawing that is inserted as part of the testdata','{"items": 1, "domain": "test drawings"}','private','tm-admin','2025-02-27 17:53:36.095164+01','tm-admin','2025-02-27 17:54:19.384961+01',28992,1);
2110 """)
2111 .update();
2112
2113 this.jdbcClient
2114 .sql(
2115 """
2116 INSERT INTO data.drawing_feature (drawing_id,id,geometry,properties) VALUES
2117 ('38faa008-013e-49d4-9528-8f58c94d8791'::uuid, '394e0d4a-b374-4d00-94c8-1758ea70e9d9'::uuid, 'SRID=28992;POLYGON ((131982.01 458929.27, 131957.31 458755.45, 132072.39 458736.69, 132099.06 458910.51, 131982.01 458929.27))'::public.geometry, '{"type": "POLYGON", "style": {"label": "", "marker": "circle", "fillColor": "rgb(117, 117, 117)", "labelSize": 15, "labelColor": "rgb(0, 0, 0)", "markerSize": 5, "fillOpacity": 30, "strokeColor": "rgb(98, 54, 255)", "strokeWidth": 3, "strokeOpacity": 100, "markerRotation": 0, "markerFillColor": "rgb(98, 54, 255)", "labelOutlineColor": "rgb(189, 189, 189)", "markerStrokeColor": "rgb(98, 54, 255)", "markerStrokeWidth": 1}}'::jsonb),
2118 ('38faa008-013e-49d4-9528-8f58c94d8791'::uuid, '89fc320c-41a3-4635-8d98-39d822339692'::uuid, 'SRID=28992;POINT (131897.55 458971.24)'::public.geometry, '{"type": "CIRCLE", "style": {"label": "CIRCLE", "marker": "circle", "fillColor": "rgb(117, 117, 117)", "labelSize": 15, "labelColor": "rgb(0, 0, 0)", "markerSize": 5, "fillOpacity": 30, "strokeColor": "rgb(98, 54, 255)", "strokeWidth": 3, "strokeOpacity": 100, "markerRotation": 0, "markerFillColor": "rgb(98, 54, 255)", "labelOutlineColor": "rgb(189, 189, 189)", "markerStrokeColor": "rgb(98, 54, 255)", "markerStrokeWidth": 1}, "radius": 14.989999999990687}'::jsonb),
2119 ('38faa008-013e-49d4-9528-8f58c94d8791'::uuid, '5d6252cc-25b6-4a43-9053-0779b86895a7'::uuid, 'SRID=28992;LINESTRING (131920.76 458754.96, 131923.73 458783.1, 131930.15 458793.97, 131945.95 458912.98, 131967.68 458946.06)'::public.geometry, '{"type": "LINE", "style": {"label": "", "marker": "circle", "fillColor": "rgb(255, 82, 82)", "labelSize": 15, "labelColor": "rgb(0, 0, 0)", "markerSize": 5, "fillOpacity": 30, "strokeColor": "rgb(255, 82, 82)", "strokeWidth": 13, "strokeOpacity": 100, "markerRotation": 0, "markerFillColor": "rgb(98, 54, 255)", "labelOutlineColor": "rgb(189, 189, 189)", "markerStrokeColor": "rgb(98, 54, 255)", "markerStrokeWidth": 1}}'::jsonb)
2120 """)
2121 .update();
2122 } catch (Exception any) {
2123 logger.error("Error inserting test drawing in data schema, some tests may fail", any);
2124 }
2125 }
2126
2127 private void insertDrawingStyle() throws IOException {
2128 Upload upload = new Upload()
2129 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2130 .setMimeType("image/svg+xml")
2131 .setFilename("ISO_7001_PI_PF_007.svg")
2132 .setContent(new ClassPathResource("test/ISO_7001_PI_PF_007.svg").getContentAsByteArray())
2133 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2134 upload = uploadRepository.save(upload);
2135 UUID drinkwaterImageId = upload.getId();
2136
2137 upload = new Upload()
2138 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2139 .setMimeType("image/svg+xml")
2140 .setFilename("lichtpunt.svg")
2141 .setContent(new ClassPathResource("test/lichtpunt.svg").getContentAsByteArray())
2142 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2143 upload = uploadRepository.save(upload);
2144 UUID lichtpuntImageId = upload.getId();
2145
2146 upload = new Upload()
2147 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2148 .setMimeType("image/svg+xml")
2149 .setFilename("ISO_7010_E003_-_First_aid_sign.svg")
2150 .setContent(new ClassPathResource("test/ISO_7010_E003_-_First_aid_sign.svg").getContentAsByteArray())
2151 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2152 upload = uploadRepository.save(upload);
2153 UUID firstAidImageId = upload.getId();
2154
2155 Properties props = new Properties();
2156 props.putAll(Map.of(
2157 "water-uuid", drinkwaterImageId.toString(),
2158 "lichtpunt-uuid", lichtpuntImageId.toString(),
2159 "first-aid-uuid", firstAidImageId.toString()));
2160
2161 String drawingStyles =
2162 new ClassPathResource("test/object-drawing-style.json").getContentAsString(StandardCharsets.UTF_8);
2163 drawingStyles = new PropertyPlaceholderHelper("${", "}").replacePlaceholders(drawingStyles, props);
2164
2165
2166 upload = new Upload()
2167 .setCategory(Upload.CATEGORY_DRAWING_STYLE)
2168 .setMimeType("application/json")
2169 .setFilename("object-drawing-style.json")
2170 .setContent(drawingStyles.getBytes(StandardCharsets.UTF_8))
2171 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2172 uploadRepository.save(upload);
2173 }
2174 }