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 java.io.IOException;
21 import java.lang.invoke.MethodHandles;
22 import java.net.URI;
23 import java.nio.charset.StandardCharsets;
24 import java.sql.SQLException;
25 import java.time.OffsetDateTime;
26 import java.time.ZoneId;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.NoSuchElementException;
32 import java.util.Optional;
33 import java.util.Properties;
34 import java.util.Set;
35 import java.util.UUID;
36 import org.apache.solr.client.solrj.SolrServerException;
37 import org.quartz.SchedulerException;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import org.springframework.beans.factory.annotation.Value;
41 import org.springframework.boot.SpringApplication;
42 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
43 import org.springframework.boot.context.event.ApplicationReadyEvent;
44 import org.springframework.context.ApplicationContext;
45 import org.springframework.context.event.EventListener;
46 import org.springframework.core.io.ClassPathResource;
47 import org.springframework.jdbc.core.simple.JdbcClient;
48 import org.springframework.transaction.annotation.Transactional;
49 import org.springframework.util.PropertyPlaceholderHelper;
50 import org.tailormap.api.admin.model.TaskSchedule;
51 import org.tailormap.api.geotools.featuresources.AttachmentsHelper;
52 import org.tailormap.api.geotools.featuresources.FeatureSourceFactoryHelper;
53 import org.tailormap.api.geotools.featuresources.JDBCFeatureSourceHelper;
54 import org.tailormap.api.geotools.featuresources.WFSFeatureSourceHelper;
55 import org.tailormap.api.persistence.Application;
56 import org.tailormap.api.persistence.Catalog;
57 import org.tailormap.api.persistence.Configuration;
58 import org.tailormap.api.persistence.GeoService;
59 import org.tailormap.api.persistence.Group;
60 import org.tailormap.api.persistence.Page;
61 import org.tailormap.api.persistence.SearchIndex;
62 import org.tailormap.api.persistence.TMFeatureSource;
63 import org.tailormap.api.persistence.TMFeatureType;
64 import org.tailormap.api.persistence.Upload;
65 import org.tailormap.api.persistence.User;
66 import org.tailormap.api.persistence.helper.GeoServiceHelper;
67 import org.tailormap.api.persistence.json.AppContent;
68 import org.tailormap.api.persistence.json.AppLayerSettings;
69 import org.tailormap.api.persistence.json.AppSettings;
70 import org.tailormap.api.persistence.json.AppTreeLayerNode;
71 import org.tailormap.api.persistence.json.AppTreeLevelNode;
72 import org.tailormap.api.persistence.json.AppTreeNode;
73 import org.tailormap.api.persistence.json.AppUiSettings;
74 import org.tailormap.api.persistence.json.AttachmentAttributeType;
75 import org.tailormap.api.persistence.json.AttributeSettings;
76 import org.tailormap.api.persistence.json.AttributeValueSettings;
77 import org.tailormap.api.persistence.json.AuthorizationRule;
78 import org.tailormap.api.persistence.json.AuthorizationRuleDecision;
79 import org.tailormap.api.persistence.json.Bounds;
80 import org.tailormap.api.persistence.json.CatalogNode;
81 import org.tailormap.api.persistence.json.FeatureTypeRef;
82 import org.tailormap.api.persistence.json.FeatureTypeTemplate;
83 import org.tailormap.api.persistence.json.Filter;
84 import org.tailormap.api.persistence.json.FilterEditConfiguration;
85 import org.tailormap.api.persistence.json.FilterGroup;
86 import org.tailormap.api.persistence.json.GeoServiceDefaultLayerSettings;
87 import org.tailormap.api.persistence.json.GeoServiceLayerSettings;
88 import org.tailormap.api.persistence.json.GeoServiceSettings;
89 import org.tailormap.api.persistence.json.JDBCConnectionProperties;
90 import org.tailormap.api.persistence.json.MenuItem;
91 import org.tailormap.api.persistence.json.PageTile;
92 import org.tailormap.api.persistence.json.ServiceAuthentication;
93 import org.tailormap.api.persistence.json.TailormapObjectRef;
94 import org.tailormap.api.persistence.json.TileLayerHiDpiMode;
95 import org.tailormap.api.persistence.json.WMSStyle;
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 import tools.jackson.core.JacksonException;
118 import tools.jackson.databind.json.JsonMapper;
119
120
121
122
123
124 @org.springframework.context.annotation.Configuration
125 @ConditionalOnProperty(name = "tailormap-api.database.populate-testdata", havingValue = "true")
126 public class PopulateTestData {
127
128 private static final Logger logger =
129 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
130 private final ApplicationContext appContext;
131 private final UserRepository userRepository;
132 private final GroupRepository groupRepository;
133 private final CatalogRepository catalogRepository;
134 private final GeoServiceRepository geoServiceRepository;
135 private final GeoServiceHelper geoServiceHelper;
136 private final SolrService solrService;
137 private final TaskManagerService taskManagerService;
138 private final FeatureSourceRepository featureSourceRepository;
139 private final ApplicationRepository applicationRepository;
140 private final ConfigurationRepository configurationRepository;
141 private final SearchIndexRepository searchIndexRepository;
142 private final FeatureSourceFactoryHelper featureSourceFactoryHelper;
143 private final UploadRepository uploadRepository;
144 private final PageRepository pageRepository;
145 private final JdbcClient jdbcClient;
146
147 @Value("${spatial.dbs.connect:false}")
148 private boolean connectToSpatialDbs;
149
150 @Value("#{'${tailormap-api.database.populate-testdata.categories}'.split(',')}")
151 private Set<String> categories;
152
153 @Value("${spatial.dbs.localhost:true}")
154 private boolean connectToSpatialDbsAtLocalhost;
155
156 @Value("${tailormap-api.database.populate-testdata.admin-hashed-password}")
157 private String adminHashedPassword;
158
159 @Value("${tailormap-api.database.populate-testdata.exit:false}")
160 private boolean exit;
161
162 @Value("${MAP5_URL:#{null}}")
163 private String map5url;
164
165 private final String tiles3dProxyUsername = "b3p";
166
167
168 private final String tiles3dProxyPassword = "cOxPlJFWmtndFk0eVg5VdL";
169
170 @Value("${tailormap-api.solr-batch-size:1000}")
171 private int solrBatchSize;
172
173 @Value("${tailormap-api.solr-geometry-validation-rule:repairBuffer0}")
174 private String solrGeometryValidationRule;
175
176 private static final String PROVINCIE_FEATURE_TYPE_NAME = "bestuurlijkegebieden:Provinciegebied";
177
178 public PopulateTestData(
179 ApplicationContext appContext,
180 UserRepository userRepository,
181 GroupRepository groupRepository,
182 CatalogRepository catalogRepository,
183 GeoServiceRepository geoServiceRepository,
184 GeoServiceHelper geoServiceHelper,
185 SolrService solrService,
186 TaskManagerService taskManagerService,
187 FeatureSourceRepository featureSourceRepository,
188 ApplicationRepository applicationRepository,
189 ConfigurationRepository configurationRepository,
190 FeatureSourceFactoryHelper featureSourceFactoryHelper,
191 SearchIndexRepository searchIndexRepository,
192 UploadRepository uploadRepository,
193 PageRepository pageRepository,
194 JdbcClient jdbcClient) {
195 this.appContext = appContext;
196 this.userRepository = userRepository;
197 this.groupRepository = groupRepository;
198 this.catalogRepository = catalogRepository;
199 this.geoServiceRepository = geoServiceRepository;
200 this.geoServiceHelper = geoServiceHelper;
201 this.solrService = solrService;
202 this.taskManagerService = taskManagerService;
203 this.featureSourceRepository = featureSourceRepository;
204 this.applicationRepository = applicationRepository;
205 this.configurationRepository = configurationRepository;
206 this.featureSourceFactoryHelper = featureSourceFactoryHelper;
207 this.searchIndexRepository = searchIndexRepository;
208 this.uploadRepository = uploadRepository;
209 this.pageRepository = pageRepository;
210 this.jdbcClient = jdbcClient;
211 }
212
213 @EventListener(ApplicationReadyEvent.class)
214 @Transactional
215 public void populate() throws Exception {
216 InternalAdminAuthentication.setInSecurityContext();
217 try {
218
219
220 createTestUsersAndGroups();
221 createConfigurationTestData();
222 if (categories.contains("catalog")) {
223 createCatalogTestData();
224 }
225 if (categories.contains("apps")) {
226 createAppTestData();
227 }
228 if (categories.contains("search-index")) {
229 try {
230 createSolrIndex();
231 } catch (Exception e) {
232 logger.error("Exception creating Solr Index for testdata (continuing)", e);
233 }
234 }
235 if (categories.contains("search-index")) {
236 createSearchIndexTasks();
237 }
238 if (categories.contains("pages")) {
239 createPages();
240 }
241 logger.info("Test entities created");
242 if (categories.contains("drawing")) {
243 insertTestDrawing();
244 logger.info("Test drawing created");
245 try {
246 insertDrawingStyle();
247 logger.info("Test drawing style created");
248 } catch (Exception e) {
249 logger.error("Exception creating drawing style", e);
250 }
251 }
252 } finally {
253 InternalAdminAuthentication.clearSecurityContextAuthentication();
254 }
255 if (exit) {
256
257
258 new Thread(() -> {
259 try {
260 logger.info("Exiting in 10 seconds");
261 Thread.sleep(10000);
262 } catch (InterruptedException ignored) {
263
264 }
265 SpringApplication.exit(appContext, () -> 0);
266 System.exit(0);
267 })
268 .start();
269 }
270 }
271
272 public void createTestUsersAndGroups() throws NoSuchElementException {
273 Group groupFoo = new Group().setName("test-foo").setDescription("Used for integration tests.");
274 groupRepository.save(groupFoo);
275
276 Group groupBar = new Group().setName("test-bar").setDescription("Used for integration tests.");
277 groupBar.addOrUpdateAdminProperty("group-property", true, true);
278 groupBar.addOrUpdateAdminProperty("group-private-property", 999.9, false);
279 groupRepository.save(groupBar);
280
281 Group groupBaz = new Group().setName("test-baz").setDescription("Used for integration tests.");
282 groupRepository.save(groupBaz);
283
284
285 User u = new User().setUsername("user").setPassword("{noop}user").setEmail("user@example.com");
286 u.getGroups().addAll(List.of(groupFoo, groupBar, groupBaz));
287 userRepository.save(u);
288
289
290 u = new User()
291 .setUsername("foo")
292 .setPassword("{noop}foo")
293 .setEmail("foo@example.com")
294 .setName("Foo only user");
295 u.getGroups().add(groupFoo);
296 userRepository.save(u);
297
298 u = new User()
299 .setUsername("disabled")
300 .setPassword("{noop}disabled")
301 .setEmail("disabled@example.com")
302 .setEnabled(false);
303 userRepository.save(u);
304
305 u = new User()
306 .setUsername("expired")
307 .setPassword("{noop}expired")
308 .setEmail("expired@example.com")
309 .setValidUntil(
310 OffsetDateTime.now(ZoneId.systemDefault()).minusDays(1).toZonedDateTime());
311 userRepository.save(u);
312
313
314 u = new User().setUsername("tm-admin").setPassword(adminHashedPassword);
315 u.addOrUpdateAdminProperty("some-property", "some-value", true);
316 u.addOrUpdateAdminProperty("admin-property", "private-value", false);
317 u.getGroups().add(groupRepository.findById(Group.ADMIN).orElseThrow());
318 u.getGroups().add(groupBar);
319 userRepository.save(u);
320 }
321
322 private final List<AuthorizationRule> ruleAnonymousRead = List.of(new AuthorizationRule()
323 .groupName(Group.ANONYMOUS)
324 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)));
325
326 private final List<AuthorizationRule> ruleLoggedIn = List.of(new AuthorizationRule()
327 .groupName(Group.AUTHENTICATED)
328 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)));
329
330 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
331 private void createCatalogTestData() throws Exception {
332 Catalog catalog = catalogRepository.findById(Catalog.MAIN).orElseThrow();
333 CatalogNode rootCatalogNode = catalog.getNodes().getFirst();
334 CatalogNode catalogNode = new CatalogNode().id("test").title("Test services");
335 rootCatalogNode.addChildrenItem(catalogNode.getId());
336 catalog.getNodes().add(catalogNode);
337
338 String osmAttribution = "© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors";
339
340 Bounds rdTileGridExtent =
341 new Bounds().minx(-285401.92).maxx(595401.92).miny(22598.08).maxy(903401.92);
342
343 Upload legend = new Upload()
344 .setCategory(Upload.CATEGORY_LEGEND)
345 .setFilename("gemeentegebied-legend.png")
346 .setMimeType("image/png")
347 .setContent(new ClassPathResource("test/gemeentegebied-legend.png").getContentAsByteArray())
348 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
349 uploadRepository.save(legend);
350
351 Collection<GeoService> services = List.of(
352 new GeoService()
353 .setId("demo")
354 .setProtocol(WMS)
355 .setTitle("Demo")
356 .setPublished(true)
357 .setAuthorizationRules(ruleAnonymousRead)
358 .setUrl("https://demo.tailormap.com/geoserver/geodata/ows?SERVICE=WMS"),
359 new GeoService()
360 .setId("osm")
361 .setProtocol(XYZ)
362 .setTitle("OSM")
363 .setUrl("https://tile.openstreetmap.org/{z}/{x}/{y}.png")
364 .setAuthorizationRules(ruleAnonymousRead)
365 .setSettings(new GeoServiceSettings()
366 .xyzCrs("EPSG:3857")
367 .layerSettings(Map.of(
368 "xyz",
369 new GeoServiceLayerSettings()
370 .attribution(osmAttribution)
371 .maxZoom(19)))),
372
373 new GeoService()
374 .setId("snapshot-geoserver")
375 .setProtocol(WMS)
376 .setTitle("Test GeoServer")
377 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
378 .setAuthorizationRules(ruleAnonymousRead)
379 .setPublished(true),
380 new GeoService()
381 .setId("filtered-snapshot-geoserver")
382 .setProtocol(WMS)
383 .setTitle("Test GeoServer (with authorization rules)")
384 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
385 .setAuthorizationRules(List.of(
386 new AuthorizationRule()
387 .groupName("test-foo")
388 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)),
389 new AuthorizationRule()
390 .groupName("test-baz")
391 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))
392 .setSettings(new GeoServiceSettings()
393 .layerSettings(Map.of(
394 "BGT",
395 new GeoServiceLayerSettings()
396 .addAuthorizationRulesItem(new AuthorizationRule()
397 .groupName("test-foo")
398 .decisions(Map.of(
399 ACCESS_TYPE_VIEW, AuthorizationRuleDecision.DENY)))
400 .addAuthorizationRulesItem(new AuthorizationRule()
401 .groupName("test-baz")
402 .decisions(Map.of(
403 ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))))
404 .setPublished(true),
405 new GeoService()
406 .setId("snapshot-geoserver-proxied")
407 .setProtocol(WMS)
408 .setTitle("Test GeoServer (proxied)")
409 .setUrl("https://snapshot.tailormap.nl/geoserver/wms")
410 .setAuthorizationRules(ruleAnonymousRead)
411 .setSettings(new GeoServiceSettings().useProxy(true)),
412 new GeoService()
413 .setId("openbasiskaart")
414 .setProtocol(WMTS)
415 .setTitle("Openbasiskaart")
416 .setUrl("https://www.openbasiskaart.nl/mapcache/wmts")
417 .setAuthorizationRules(ruleAnonymousRead)
418 .setSettings(new GeoServiceSettings()
419 .defaultLayerSettings(new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
420 .layerSettings(Map.of(
421 "osm",
422 new GeoServiceLayerSettings()
423 .title("Openbasiskaart")
424 .hiDpiDisabled(false)
425 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
426 .hiDpiSubstituteLayer("osm-hq")))),
427 new GeoService()
428 .setId("openbasiskaart-proxied")
429 .setProtocol(WMTS)
430 .setTitle("Openbasiskaart (proxied)")
431 .setUrl("https://www.openbasiskaart.nl/mapcache/wmts")
432 .setAuthorizationRules(ruleAnonymousRead)
433
434
435 .setAuthentication(new ServiceAuthentication()
436 .method(ServiceAuthentication.MethodEnum.PASSWORD)
437 .username("test")
438 .password("test"))
439 .setSettings(new GeoServiceSettings()
440 .useProxy(true)
441 .defaultLayerSettings(new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
442 .layerSettings(Map.of(
443 "osm",
444 new GeoServiceLayerSettings()
445 .hiDpiDisabled(false)
446 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
447 .hiDpiSubstituteLayer("osm-hq")))),
448 new GeoService()
449 .setId("openbasiskaart-tms")
450 .setProtocol(XYZ)
451 .setTitle("Openbasiskaart (TMS)")
452 .setUrl("https://openbasiskaart.nl/mapcache/tms/1.0.0/osm@rd/{z}/{x}/{-y}.png")
453 .setAuthorizationRules(ruleAnonymousRead)
454 .setSettings(
455 new GeoServiceSettings()
456 .xyzCrs("EPSG:28992")
457 .defaultLayerSettings(
458 new GeoServiceDefaultLayerSettings().attribution(osmAttribution))
459 .layerSettings(
460 Map.of(
461 "xyz",
462 new GeoServiceLayerSettings()
463 .maxZoom(15)
464 .tileGridExtent(rdTileGridExtent)
465 .hiDpiDisabled(false)
466 .hiDpiMode(
467 TileLayerHiDpiMode
468 .SUBSTITUTELAYERTILEPIXELRATIOONLY)
469 .hiDpiSubstituteLayer(
470 "https://openbasiskaart.nl/mapcache/tms/1.0.0/osm-hq@rd-hq/{z}/{x}/{-y}.png")))),
471 new GeoService()
472 .setId("pdok-hwh-luchtfotorgb")
473 .setProtocol(WMTS)
474 .setTitle("PDOK HWH luchtfoto")
475 .setUrl("https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0")
476 .setAuthorizationRules(ruleAnonymousRead)
477 .setPublished(true)
478 .setSettings(new GeoServiceSettings()
479 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
480 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl)")
481 .hiDpiDisabled(false))
482 .putLayerSettingsItem(
483 "Actueel_orthoHR", new GeoServiceLayerSettings().title("Luchtfoto"))),
484 new GeoService()
485 .setId("b3p-mapproxy-luchtfoto")
486 .setProtocol(XYZ)
487 .setTitle("Luchtfoto (TMS)")
488 .setUrl("https://mapproxy.b3p.nl/tms/1.0.0/luchtfoto/EPSG28992/{z}/{x}/{-y}.jpeg")
489 .setAuthorizationRules(ruleAnonymousRead)
490 .setPublished(true)
491 .setSettings(new GeoServiceSettings()
492 .xyzCrs("EPSG:28992")
493 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
494 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl)")
495 .hiDpiDisabled(false))
496 .layerSettings(Map.of(
497 "xyz",
498 new GeoServiceLayerSettings()
499 .maxZoom(14)
500 .tileGridExtent(rdTileGridExtent)
501 .hiDpiMode(TileLayerHiDpiMode.SHOWNEXTZOOMLEVEL)))),
502 new GeoService()
503 .setId("at-basemap")
504 .setProtocol(WMTS)
505 .setTitle("basemap.at")
506 .setUrl("https://mapsneu.wien.gv.at/basemapneu/1.0.0/WMTSCapabilities.xml")
507 .setAuthorizationRules(ruleAnonymousRead)
508 .setPublished(true)
509 .setSettings(new GeoServiceSettings()
510 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
511 .attribution("© [basemap.at](https://basemap.at)")
512 .hiDpiDisabled(true))
513 .layerSettings(Map.of(
514 "geolandbasemap",
515 new GeoServiceLayerSettings()
516 .title("Basemap")
517 .hiDpiDisabled(false)
518 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERTILEPIXELRATIOONLY)
519 .hiDpiSubstituteLayer("bmaphidpi"),
520 "bmaporthofoto30cm",
521 new GeoServiceLayerSettings()
522 .title("Orthophoto")
523 .hiDpiDisabled(false)))),
524 new GeoService()
525 .setId("pdok-kadaster-bestuurlijkegebieden")
526 .setProtocol(WMS)
527 .setUrl("https://service.pdok.nl/kadaster/brk-bestuurlijke-gebieden/wms/v1_0?service=WMS")
528 .setAuthorizationRules(ruleAnonymousRead)
529 .setSettings(new GeoServiceSettings()
530 .defaultLayerSettings(new GeoServiceDefaultLayerSettings()
531 .description("This layer shows an administrative boundary."))
532
533 .serverType(GeoServiceSettings.ServerTypeEnum.MAPSERVER)
534 .useProxy(true)
535 .putLayerSettingsItem(
536 "Gemeentegebied",
537 new GeoServiceLayerSettings()
538 .legendImageId(legend.getId().toString())))
539 .setPublished(true)
540 .setTitle("PDOK Kadaster bestuurlijke gebieden"),
541 new GeoService()
542 .setId("bestuurlijkegebieden-proxied")
543 .setProtocol(WMS)
544 .setUrl("https://service.pdok.nl/kadaster/brk-bestuurlijke-gebieden/wms/v1_0?service=WMS")
545 .setAuthorizationRules(ruleAnonymousRead)
546
547
548
549 .setAuthentication(new ServiceAuthentication()
550 .method(ServiceAuthentication.MethodEnum.PASSWORD)
551 .username("test")
552 .password("test"))
553 .setSettings(new GeoServiceSettings()
554
555 .serverType(GeoServiceSettings.ServerTypeEnum.MAPSERVER)
556 .useProxy(true))
557 .setPublished(true)
558 .setTitle("Bestuurlijke gebieden (proxied met auth)"),
559 new GeoService()
560 .setId("3dbag_utrecht")
561 .setProtocol(TILES3D)
562 .setUrl("https://3dtilesnederland.nl/tiles/1.0/implicit/nederland/344.json")
563 .setTitle("3D BAG Utrecht")
564 .setPublished(true)
565 .setAuthorizationRules(ruleAnonymousRead),
566 new GeoService()
567 .setId("ahn_terrain_model")
568 .setProtocol(QUANTIZEDMESH)
569 .setUrl(
570 "https://api.pdok.nl/kadaster/3d-basisvoorziening/ogc/v1/collections/digitaalterreinmodel")
571 .setTitle("AHN Terrain Model")
572 .setPublished(true)
573 .setAuthorizationRules(ruleAnonymousRead),
574 new GeoService()
575 .setId("3d_basisvoorziening_gebouwen_proxy")
576 .setProtocol(TILES3D)
577 .setUrl(
578 "https://api.pdok.nl/kadaster/3d-basisvoorziening/ogc/v1_0/collections/gebouwen/3dtiles")
579 .setTitle("3D Basisvoorziening Gebouwen Proxy")
580 .setPublished(true)
581 .setAuthorizationRules(ruleAnonymousRead)
582 .setSettings(new GeoServiceSettings().useProxy(true)),
583 new GeoService()
584 .setId("3d_utrecht_proxied_auth")
585 .setProtocol(TILES3D)
586 .setUrl("https://snapshot.tailormap.nl/tiles3d-proxy/3dtiles")
587 .setTitle("3D Utrecht Proxied with Authorization")
588 .setPublished(true)
589 .setAuthorizationRules(ruleAnonymousRead)
590 .setAuthentication(new ServiceAuthentication()
591 .method(ServiceAuthentication.MethodEnum.PASSWORD)
592 .username(tiles3dProxyUsername)
593 .password(tiles3dProxyPassword))
594 .setSettings(new GeoServiceSettings().useProxy(true))
595
596 );
597
598 if (map5url != null) {
599 GeoServiceLayerSettings osmAttr = new GeoServiceLayerSettings().attribution(osmAttribution);
600 GeoServiceLayerSettings map5Attr = new GeoServiceLayerSettings()
601 .attribution("Kaarten: [Map5.nl](https://map5.nl), data: " + osmAttribution);
602 services = new ArrayList<>(services);
603 services.add(new GeoService()
604 .setId("map5")
605 .setProtocol(WMTS)
606 .setTitle("Map5")
607 .setUrl(map5url)
608 .setAuthorizationRules(ruleAnonymousRead)
609 .setSettings(new GeoServiceSettings()
610 .defaultLayerSettings(new GeoServiceDefaultLayerSettings().hiDpiDisabled(true))
611 .layerSettings(Map.of(
612 "openlufo",
613 new GeoServiceLayerSettings()
614 .attribution("© [Beeldmateriaal.nl](https://beeldmateriaal.nl), "
615 + osmAttribution),
616 "luforoadslabels",
617 osmAttr,
618 "map5topo",
619 new GeoServiceLayerSettings()
620 .attribution(map5Attr.getAttribution())
621 .hiDpiDisabled(false)
622 .hiDpiMode(TileLayerHiDpiMode.SUBSTITUTELAYERSHOWNEXTZOOMLEVEL)
623 .hiDpiSubstituteLayer("map5topo_hq"),
624 "map5topo_gray",
625 map5Attr,
626 "map5topo_simple",
627 map5Attr,
628 "map5topo_simple_gray",
629 map5Attr,
630 "opensimpletopo",
631 osmAttr,
632 "opensimpletopo_gray",
633 osmAttr,
634 "opentopo",
635 osmAttr,
636 "opentopo_gray",
637 osmAttr))));
638 }
639
640 for (GeoService geoService : services) {
641 try {
642 geoServiceHelper.loadServiceCapabilities(geoService);
643 } catch (Exception e) {
644 logger.error(
645 "Error loading capabilities for {} service URL {}: {}, {}",
646 geoService.getProtocol().getValue(),
647 geoService.getUrl(),
648 e.getClass(),
649 e.getMessage());
650 }
651
652 geoServiceRepository.save(geoService);
653 catalogNode.addItemsItem(new TailormapObjectRef()
654 .kind(TailormapObjectRef.KindEnum.GEO_SERVICE)
655 .id(geoService.getId()));
656 }
657
658 CatalogNode wfsFeatureSourceCatalogNode =
659 new CatalogNode().id("wfs_feature_sources").title("WFS feature sources");
660 rootCatalogNode.addChildrenItem(wfsFeatureSourceCatalogNode.getId());
661 catalog.getNodes().add(wfsFeatureSourceCatalogNode);
662
663 services.stream().filter(s -> s.getProtocol() == WMS).forEach(s -> {
664 geoServiceHelper.findAndSaveRelatedWFS(s);
665 List<TMFeatureSource> linkedSources = featureSourceRepository.findByLinkedServiceId(s.getId());
666 for (TMFeatureSource linkedSource : linkedSources) {
667 wfsFeatureSourceCatalogNode.addItemsItem(new TailormapObjectRef()
668 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
669 .id(linkedSource.getId().toString()));
670 }
671 });
672
673 String geodataPassword = "980f1c8A-25933b2";
674
675 Map<String, TMFeatureSource> featureSources = Map.of(
676 "postgis",
677 new TMFeatureSource()
678 .setProtocol(TMFeatureSource.Protocol.JDBC)
679 .setTitle("PostGIS")
680 .setJdbcConnection(new JDBCConnectionProperties()
681 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
682 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
683 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
684 .database("geodata")
685 .schema("public")
686 .additionalProperties(Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
687 .setAuthentication(new ServiceAuthentication()
688 .method(ServiceAuthentication.MethodEnum.PASSWORD)
689 .username("geodata")
690 .password(geodataPassword)),
691 "postgis_osm",
692 new TMFeatureSource()
693 .setProtocol(TMFeatureSource.Protocol.JDBC)
694 .setTitle("PostGIS OSM")
695 .setJdbcConnection(new JDBCConnectionProperties()
696 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
697 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
698 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
699 .database("geodata")
700 .schema("osm")
701 .additionalProperties(Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
702 .setAuthentication(new ServiceAuthentication()
703 .method(ServiceAuthentication.MethodEnum.PASSWORD)
704 .username("geodata")
705 .password(geodataPassword)),
706 "oracle",
707 new TMFeatureSource()
708 .setProtocol(TMFeatureSource.Protocol.JDBC)
709 .setTitle("Oracle")
710 .setJdbcConnection(new JDBCConnectionProperties()
711 .dbtype(JDBCConnectionProperties.DbtypeEnum.ORACLE)
712 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "oracle")
713 .database("/FREEPDB1")
714 .schema("GEODATA")
715 .additionalProperties(Map.of("connectionOptions", "?oracle.jdbc.J2EE13Compliant=true")))
716 .setAuthentication(new ServiceAuthentication()
717 .method(ServiceAuthentication.MethodEnum.PASSWORD)
718 .username("geodata")
719 .password(geodataPassword)),
720 "sqlserver",
721 new TMFeatureSource()
722 .setProtocol(TMFeatureSource.Protocol.JDBC)
723 .setTitle("MS SQL Server")
724 .setJdbcConnection(new JDBCConnectionProperties()
725 .dbtype(JDBCConnectionProperties.DbtypeEnum.SQLSERVER)
726 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "sqlserver")
727 .database("geodata")
728 .schema("dbo")
729 .additionalProperties(Map.of("connectionOptions", ";encrypt=false")))
730 .setAuthentication(new ServiceAuthentication()
731 .method(ServiceAuthentication.MethodEnum.PASSWORD)
732 .username("geodata")
733 .password(geodataPassword)),
734 "pdok-kadaster-bestuurlijkegebieden",
735 new TMFeatureSource()
736 .setProtocol(TMFeatureSource.Protocol.WFS)
737 .setUrl(
738 "https://service.pdok.nl/kadaster/brk-bestuurlijke-gebieden/wfs/v1_0?service=WFS&VERSION=2.0.0")
739 .setTitle("Bestuurlijke gebieden")
740 .setNotes(
741 "Overzicht van de bestuurlijke indeling van Nederland in gemeenten en provincies alsmede de rijksgrens. Gegevens zijn afgeleid uit de Basisregistratie Kadaster (BRK)."));
742 featureSourceRepository.saveAll(featureSources.values());
743
744 new WFSFeatureSourceHelper().loadCapabilities(featureSources.get("pdok-kadaster-bestuurlijkegebieden"));
745 geoServiceRepository.findById("pdok-kadaster-bestuurlijkegebieden").ifPresent(geoService -> {
746 geoService
747 .getSettings()
748 .getLayerSettings()
749 .put(
750 "Provinciegebied",
751 new GeoServiceLayerSettings()
752 .description("The administrative boundary of Dutch Provinces, connected to a WFS.")
753 .featureType(new FeatureTypeRef()
754 .featureSourceId(featureSources
755 .get("pdok-kadaster-bestuurlijkegebieden")
756 .getId())
757 .featureTypeName(PROVINCIE_FEATURE_TYPE_NAME))
758 .title("Provinciegebied (WFS)"));
759 geoServiceRepository.save(geoService);
760 });
761
762 geoServiceRepository.findById("bestuurlijkegebieden-proxied").ifPresent(geoService -> {
763 geoService
764 .getSettings()
765 .getLayerSettings()
766 .put(
767 "Provinciegebied",
768 new GeoServiceLayerSettings()
769 .featureType(new FeatureTypeRef()
770 .featureSourceId(featureSources
771 .get("pdok-kadaster-bestuurlijkegebieden")
772 .getId())
773 .featureTypeName(PROVINCIE_FEATURE_TYPE_NAME))
774 .title("Provinciegebied (WFS, proxied met auth)"));
775 geoServiceRepository.save(geoService);
776 });
777
778 CatalogNode featureSourceCatalogNode =
779 new CatalogNode().id("feature_sources").title("Test feature sources");
780 rootCatalogNode.addChildrenItem(featureSourceCatalogNode.getId());
781 catalog.getNodes().add(featureSourceCatalogNode);
782
783 for (TMFeatureSource featureSource : featureSources.values()) {
784 featureSourceCatalogNode.addItemsItem(new TailormapObjectRef()
785 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
786 .id(featureSource.getId().toString()));
787 }
788 catalogRepository.save(catalog);
789
790 if (connectToSpatialDbs) {
791 featureSources.values().forEach(fs -> {
792 try {
793 if (fs.getProtocol() == TMFeatureSource.Protocol.JDBC) {
794 new JDBCFeatureSourceHelper().loadCapabilities(fs);
795 } else if (fs.getProtocol() == TMFeatureSource.Protocol.WFS) {
796 new WFSFeatureSourceHelper().loadCapabilities(fs);
797 }
798 } catch (Exception e) {
799 logger.error("Error loading capabilities for feature source {}", fs.getTitle(), e);
800 }
801 });
802
803 services.stream()
804
805
806 .filter(s -> s.getId().startsWith("snapshot-geoserver"))
807 .forEach(s -> s.getSettings()
808 .layerSettings(Map.of(
809 "postgis:begroeidterreindeel",
810 new GeoServiceLayerSettings()
811 .description("""
812 This layer shows data from https:
813
814 https:
815 .featureType(new FeatureTypeRef()
816 .featureSourceId(featureSources
817 .get("postgis")
818 .getId())
819 .featureTypeName("begroeidterreindeel")),
820 "postgis:bak",
821 new GeoServiceLayerSettings()
822 .featureType(new FeatureTypeRef()
823 .featureSourceId(featureSources
824 .get("postgis")
825 .getId())
826 .featureTypeName("bak")),
827 "postgis:kadastraal_perceel",
828 new GeoServiceLayerSettings()
829 .description("cadastral parcel label points")
830 .featureType(new FeatureTypeRef()
831 .featureSourceId(featureSources
832 .get("postgis")
833 .getId())
834 .featureTypeName("kadastraal_perceel")),
835 "sqlserver:wegdeel",
836 new GeoServiceLayerSettings()
837 .attribution(
838 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)")
839 .description("""
840 This layer shows data from [MS SQL Server](https:
841
842 https:
843 .featureType(new FeatureTypeRef()
844 .featureSourceId(featureSources
845 .get("sqlserver")
846 .getId())
847 .featureTypeName("wegdeel")),
848 "oracle:WATERDEEL",
849 new GeoServiceLayerSettings()
850 .description("This layer shows data from Oracle Spatial.")
851 .featureType(new FeatureTypeRef()
852 .featureSourceId(featureSources
853 .get("oracle")
854 .getId())
855 .featureTypeName("WATERDEEL")),
856 "postgis:osm_polygon",
857 new GeoServiceLayerSettings()
858 .description("This layer shows OSM data from postgis.")
859 .featureType(new FeatureTypeRef()
860 .featureSourceId(featureSources
861 .get("postgis_osm")
862 .getId())
863 .featureTypeName("osm_polygon")))));
864 }
865
866 featureSources.get("pdok-kadaster-bestuurlijkegebieden").getFeatureTypes().stream()
867 .filter(ft -> ft.getName().equals(PROVINCIE_FEATURE_TYPE_NAME))
868 .findFirst()
869 .ifPresent(ft -> {
870 ft.getSettings().addHideAttributesItem("identificatie");
871 ft.getSettings().addHideAttributesItem("ligtInLandCode");
872 ft.getSettings().addHideAttributesItem("fuuid");
873 ft.getSettings().putAttributeSettingsItem("naam", new AttributeSettings().title("Naam"));
874 ft.getSettings()
875 .setTemplate(new FeatureTypeTemplate()
876 .templateLanguage("simple")
877 .markupLanguage("markdown")
878 .template("""
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 .selectedStyles(
1302 List.of(
1303 new WMSStyle()
1304 .name("begroeidterreindeel")
1305 .title("Visualisatie van de begroeide terreindelen")
1306 .abstractText(
1307 "Deze stylesheet bevat de regels voor de visualisatie van het objecttype Begroeid Terreindeel")
1308 .legendUrl(
1309 URI.create(
1310 "https://snapshot.tailormap.nl/geoserver/ows?service=WMS&version=1.3.0&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer=test%3Apostgis_begroeidterreindeel")),
1311 new WMSStyle()
1312 .name("purple_polygon")
1313 .title("purple_polygon")
1314 .abstractText(null)
1315 .legendUrl(
1316 URI.create(
1317 "https://snapshot.tailormap.nl/geoserver/ows?service=WMS&version=1.3.0&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer=test%3Apostgis_begroeidterreindeel&style=purple_polygon")))))
1318 .putLayerSettingsItem(
1319 "lyr:snapshot-geoserver:postgis:kadastraal_perceel",
1320 new AppLayerSettings().editable(true).addReadOnlyAttributesItem("aanduiding"))
1321 .putLayerSettingsItem(
1322 "lyr:snapshot-geoserver:sqlserver:wegdeel", new AppLayerSettings().editable(true))
1323 .putLayerSettingsItem(
1324 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel",
1325 new AppLayerSettings().editable(false))
1326 .putLayerSettingsItem(
1327 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1328 new AppLayerSettings()
1329 .hiddenFunctionality(Set.of(FEATURE_INFO, ATTRIBUTE_LIST, EXPORT)))
1330 .addFilterGroupsItem(new FilterGroup()
1331 .id("filtergroup1")
1332 .source("PRESET")
1333 .type(FilterGroup.TypeEnum.ATTRIBUTE)
1334 .layerIds(List.of("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1335 .operator(FilterGroup.OperatorEnum.AND)
1336 .addFiltersItem(new Filter()
1337 .id("filter1")
1338 .type(Filter.TypeEnum.ATTRIBUTE)
1339 .condition(Filter.ConditionEnum.BEFORE)
1340 .addValueItem("2025-06-05")
1341 .attribute("creationdate")
1342 .attributeType(Filter.AttributeTypeEnum.DATE))
1343 .addFiltersItem(new Filter()
1344 .id("filter2")
1345 .type(Filter.TypeEnum.ATTRIBUTE)
1346 .condition(Filter.ConditionEnum.UNIQUE_VALUES)
1347 .addValueItem("bodembedekkers")
1348 .addValueItem("bosplantsoen")
1349 .addValueItem("gras- en kruidachtigen")
1350 .attribute("plus_fysiekvoorkomen")
1351 .attributeType(Filter.AttributeTypeEnum.STRING)
1352 .editConfiguration(new FilterEditConfiguration()
1353 .filterTool(FilterEditConfiguration.FilterToolEnum.CHECKBOX)
1354 .attributeValuesSettings(List.of(
1355 new AttributeValueSettings()
1356 .value("bodembedekkers")
1357 .initiallySelected(true)
1358 .selectable(true)
1359 .alias("Bodembedekkers"),
1360 new AttributeValueSettings()
1361 .value("bosplantsoen")
1362 .initiallySelected(true)
1363 .selectable(true)
1364 .alias("Bosplantsoen"),
1365 new AttributeValueSettings()
1366 .value("gras- en kruidachtigen")
1367 .initiallySelected(true)
1368 .selectable(true)
1369 .alias("Gras- en kruidachtigen"),
1370 new AttributeValueSettings()
1371 .value("griend en hakhout")
1372 .initiallySelected(false)
1373 .selectable(true),
1374 new AttributeValueSettings()
1375 .value("heesters")
1376 .initiallySelected(false)
1377 .selectable(true),
1378 new AttributeValueSettings()
1379 .value("planten")
1380 .initiallySelected(false)
1381 .selectable(true),
1382 new AttributeValueSettings()
1383 .value("struikrozen")
1384 .initiallySelected(false)
1385 .selectable(true),
1386 new AttributeValueSettings()
1387 .value("waardeOnbekend")
1388 .initiallySelected(false)
1389 .selectable(true))))))
1390 .addFilterGroupsItem(new FilterGroup()
1391 .id("filtergroup2")
1392 .source("PRESET")
1393 .type(FilterGroup.TypeEnum.ATTRIBUTE)
1394 .layerIds(List.of("lyr:snapshot-geoserver:postgis:kadastraal_perceel"))
1395 .operator(FilterGroup.OperatorEnum.AND)
1396 .addFiltersItem(new Filter()
1397 .id("filter3")
1398 .type(Filter.TypeEnum.ATTRIBUTE)
1399 .condition(Filter.ConditionEnum.u)
1400 .addValueItem("1")
1401 .addValueItem("12419")
1402 .attribute("perceelnummer")
1403 .attributeType(Filter.AttributeTypeEnum.DOUBLE)
1404 .editConfiguration(new FilterEditConfiguration()
1405 .filterTool(FilterEditConfiguration.FilterToolEnum.SLIDER)
1406 .initialLowerValue(1d)
1407 .initialUpperValue(12419d)
1408 .minimumValue(1d)
1409 .maximumValue(12419d)))));
1410
1411 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1412 app.setInitialExtent(
1413 new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d));
1414 app.setMaxExtent(new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d));
1415
1416 if (map5url != null) {
1417 AppTreeLevelNode root =
1418 (AppTreeLevelNode) app.getContentRoot().getBaseLayerNodes().getFirst();
1419 List<String> childrenIds = new ArrayList<>(root.getChildrenIds());
1420 childrenIds.add("lyr:map5:map5topo");
1421 childrenIds.add("lyr:map5:map5topo_simple");
1422 childrenIds.add("lvl:luchtfoto-labels");
1423 root.setChildrenIds(childrenIds);
1424 app.getSettings()
1425 .putLayerSettingsItem("lyr:map5:map5topo", new AppLayerSettings().title("Map5"))
1426 .putLayerSettingsItem("lyr:map5:map5topo_simple", new AppLayerSettings().title("Map5 simple"));
1427 app.getContentRoot()
1428 .addBaseLayerNodesItem(new AppTreeLayerNode()
1429 .objectType("AppTreeLayerNode")
1430 .id("lyr:map5:map5topo")
1431 .serviceId("map5")
1432 .layerName("map5topo")
1433 .visible(false))
1434 .addBaseLayerNodesItem(new AppTreeLayerNode()
1435 .objectType("AppTreeLayerNode")
1436 .id("lyr:map5:map5topo_simple")
1437 .serviceId("map5")
1438 .layerName("map5topo_simple")
1439 .visible(false))
1440 .addBaseLayerNodesItem(new AppTreeLevelNode()
1441 .objectType("AppTreeLevelNode")
1442 .id("lvl:luchtfoto-labels")
1443 .title("Luchtfoto met labels")
1444 .addChildrenIdsItem("lyr:map5:luforoadslabels")
1445 .addChildrenIdsItem("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2"))
1446 .addBaseLayerNodesItem(new AppTreeLayerNode()
1447 .objectType("AppTreeLayerNode")
1448 .id("lyr:map5:luforoadslabels")
1449 .serviceId("map5")
1450 .layerName("luforoadslabels")
1451 .visible(false))
1452 .addBaseLayerNodesItem(new AppTreeLayerNode()
1453 .objectType("AppTreeLayerNode")
1454 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2")
1455 .serviceId("pdok-hwh-luchtfotorgb")
1456 .layerName("Actueel_orthoHR")
1457 .visible(false));
1458 }
1459
1460 applicationRepository.save(app);
1461
1462 app = new Application()
1463 .setName("base")
1464 .setTitle("Service base app")
1465 .setCrs("EPSG:28992")
1466 .setAuthorizationRules(ruleAnonymousRead)
1467 .setContentRoot(new AppContent()
1468 .addBaseLayerNodesItem(new AppTreeLevelNode()
1469 .objectType("AppTreeLevelNode")
1470 .id("root-base-layers")
1471 .root(true)
1472 .title("Base layers")
1473 .childrenIds(List.of(
1474 "lyr:openbasiskaart:osm", "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR"))));
1475 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1476 applicationRepository.save(app);
1477
1478 app = new Application()
1479 .setName("secured")
1480 .setTitle("secured app")
1481 .setCrs("EPSG:28992")
1482 .setAuthorizationRules(ruleLoggedIn)
1483 .setContentRoot(new AppContent()
1484 .addBaseLayerNodesItem(new AppTreeLevelNode()
1485 .objectType("AppTreeLevelNode")
1486 .id("root-base-layers")
1487 .root(true)
1488 .title("Base layers")
1489 .childrenIds(List.of(
1490 "lyr:openbasiskaart:osm",
1491 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1492 "lyr:openbasiskaart-proxied:osm")))
1493 .addBaseLayerNodesItem(new AppTreeLayerNode()
1494 .objectType("AppTreeLayerNode")
1495 .id("lyr:openbasiskaart-proxied:osm")
1496 .serviceId("openbasiskaart-proxied")
1497 .layerName("osm")
1498 .visible(false))
1499 .addLayerNodesItem(new AppTreeLevelNode()
1500 .objectType("AppTreeLevelNode")
1501 .id("root")
1502 .root(true)
1503 .title("Layers")
1504 .childrenIds(List.of(
1505 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1506 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1507 "lvl:proxied")))
1508 .addLayerNodesItem(new AppTreeLayerNode()
1509 .objectType("AppTreeLayerNode")
1510 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1511 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1512 .layerName("Gemeentegebied")
1513 .visible(true))
1514 .addLayerNodesItem(new AppTreeLayerNode()
1515 .objectType("AppTreeLayerNode")
1516 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1517 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1518 .layerName("Provinciegebied")
1519 .visible(false))
1520 .addLayerNodesItem(new AppTreeLevelNode()
1521 .objectType("AppTreeLevelNode")
1522 .id("lvl:proxied")
1523 .title("Proxied")
1524 .childrenIds(List.of("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1525 .addLayerNodesItem(new AppTreeLayerNode()
1526 .objectType("AppTreeLayerNode")
1527 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1528 .serviceId("snapshot-geoserver-proxied")
1529 .layerName("postgis:begroeidterreindeel")
1530 .visible(false)))
1531 .setSettings(
1532 new AppSettings()
1533 .putLayerSettingsItem(
1534 "lyr:openbasiskaart-proxied:osm",
1535 new AppLayerSettings().title("Openbasiskaart (proxied)"))
1536 .putLayerSettingsItem(
1537 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel",
1538 new AppLayerSettings()
1539 .description("This layer should render using purple polygons")
1540 .selectedStyles(
1541 List.of(
1542 new WMSStyle()
1543 .name("purple_polygon")
1544 .title("purple_polygon")
1545 .abstractText(null)
1546 .legendUrl(
1547 URI.create(
1548 "https://snapshot.tailormap.nl/geoserver/ows?service=WMS&version=1.3.0&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer=test%3Apostgis_begroeidterreindeel&style=purple_polygon"))))));
1549
1550 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1551 applicationRepository.save(app);
1552
1553 app = new Application()
1554 .setName("secured-auth")
1555 .setTitle("secured (with authorizations)")
1556 .setCrs("EPSG:28992")
1557 .setAuthorizationRules(List.of(
1558 new AuthorizationRule()
1559 .groupName("test-foo")
1560 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)),
1561 new AuthorizationRule()
1562 .groupName("test-bar")
1563 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))
1564 .setContentRoot(new AppContent()
1565 .addLayerNodesItem(new AppTreeLevelNode()
1566 .objectType("AppTreeLevelNode")
1567 .id("root")
1568 .root(true)
1569 .title("Layers")
1570 .childrenIds(List.of("lvl:needs-auth", "lvl:public")))
1571 .addLayerNodesItem(new AppTreeLevelNode()
1572 .objectType("AppTreeLevelNode")
1573 .id("lvl:public")
1574 .title("Public")
1575 .childrenIds(List.of("lyr:snapshot-geoserver:BGT")))
1576 .addLayerNodesItem(new AppTreeLevelNode()
1577 .objectType("AppTreeLevelNode")
1578 .id("lvl:needs-auth")
1579 .title("Needs auth")
1580 .childrenIds(List.of(
1581 "lyr:filtered-snapshot-geoserver:BGT",
1582 "lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1583 .addLayerNodesItem(new AppTreeLayerNode()
1584 .objectType("AppTreeLayerNode")
1585 .id("lyr:filtered-snapshot-geoserver:BGT")
1586 .serviceId("filtered-snapshot-geoserver")
1587 .layerName("BGT")
1588 .visible(true))
1589 .addLayerNodesItem(new AppTreeLayerNode()
1590 .objectType("AppTreeLayerNode")
1591 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1592 .serviceId("filtered-snapshot-geoserver")
1593 .layerName("postgis:begroeidterreindeel")
1594 .visible(true))
1595 .addLayerNodesItem(new AppTreeLayerNode()
1596 .objectType("AppTreeLayerNode")
1597 .id("lyr:snapshot-geoserver:BGT")
1598 .serviceId("snapshot-geoserver")
1599 .layerName("BGT")
1600 .visible(true)));
1601
1602 applicationRepository.save(app);
1603
1604 app = new Application()
1605 .setName("austria")
1606 .setCrs("EPSG:3857")
1607 .setAuthorizationRules(ruleAnonymousRead)
1608 .setTitle("Austria")
1609 .setInitialExtent(
1610 new Bounds().minx(987982d).miny(5799551d).maxx(1963423d).maxy(6320708d))
1611 .setMaxExtent(
1612 new Bounds().minx(206516d).miny(5095461d).maxx(3146930d).maxy(7096232d))
1613 .setContentRoot(new AppContent()
1614 .addBaseLayerNodesItem(new AppTreeLevelNode()
1615 .objectType("AppTreeLevelNode")
1616 .id("root-base-layers")
1617 .root(true)
1618 .title("Base layers")
1619 .childrenIds(List.of(
1620 "lyr:at-basemap:geolandbasemap",
1621 "lyr:at-basemap:orthofoto",
1622 "lvl:orthofoto-labels",
1623 "lyr:osm:xyz")))
1624 .addBaseLayerNodesItem(new AppTreeLayerNode()
1625 .objectType("AppTreeLayerNode")
1626 .id("lyr:at-basemap:geolandbasemap")
1627 .serviceId("at-basemap")
1628 .layerName("geolandbasemap")
1629 .visible(true))
1630 .addBaseLayerNodesItem(new AppTreeLayerNode()
1631 .objectType("AppTreeLayerNode")
1632 .id("lyr:at-basemap:orthofoto")
1633 .serviceId("at-basemap")
1634 .layerName("bmaporthofoto30cm")
1635 .visible(false))
1636 .addBaseLayerNodesItem(new AppTreeLevelNode()
1637 .objectType("AppTreeLevelNode")
1638 .id("lvl:orthofoto-labels")
1639 .title("Orthophoto with labels")
1640 .childrenIds(List.of("lyr:at-basemap:bmapoverlay", "lyr:at-basemap:orthofoto_2")))
1641 .addBaseLayerNodesItem(new AppTreeLayerNode()
1642 .objectType("AppTreeLayerNode")
1643 .id("lyr:at-basemap:bmapoverlay")
1644 .serviceId("at-basemap")
1645 .layerName("bmapoverlay")
1646 .visible(false))
1647 .addBaseLayerNodesItem(new AppTreeLayerNode()
1648 .objectType("AppTreeLayerNode")
1649 .id("lyr:at-basemap:orthofoto_2")
1650 .serviceId("at-basemap")
1651 .layerName("bmaporthofoto30cm")
1652 .visible(false))
1653 .addBaseLayerNodesItem(new AppTreeLayerNode()
1654 .objectType("AppTreeLayerNode")
1655 .id("lyr:osm:xyz")
1656 .serviceId("osm")
1657 .layerName("xyz")
1658 .visible(false)));
1659
1660 applicationRepository.save(app);
1661
1662 app = new Application()
1663 .setName("3d_utrecht")
1664 .setCrs("EPSG:3857")
1665 .setAuthorizationRules(ruleLoggedIn)
1666 .setTitle("3D Utrecht")
1667 .setInitialExtent(
1668 new Bounds().minx(558390d).miny(6818485d).maxx(566751d).maxy(6824036d))
1669 .setMaxExtent(
1670 new Bounds().minx(91467d).miny(6496479d).maxx(1037043d).maxy(7147453d))
1671 .setSettings(new AppSettings().uiSettings(new AppUiSettings().enable3D(true)))
1672 .setContentRoot(new AppContent()
1673 .addBaseLayerNodesItem(new AppTreeLevelNode()
1674 .objectType("AppTreeLevelNode")
1675 .id("root-base-layers")
1676 .root(true)
1677 .title("Base layers")
1678 .childrenIds(List.of("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR", "lyr:osm:xyz")))
1679 .addBaseLayerNodesItem(new AppTreeLayerNode()
1680 .objectType("AppTreeLayerNode")
1681 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR")
1682 .serviceId("pdok-hwh-luchtfotorgb")
1683 .layerName("Actueel_orthoHR")
1684 .visible(true))
1685 .addBaseLayerNodesItem(new AppTreeLayerNode()
1686 .objectType("AppTreeLayerNode")
1687 .id("lyr:osm:xyz")
1688 .serviceId("osm")
1689 .layerName("xyz")
1690 .visible(false))
1691 .addLayerNodesItem(new AppTreeLevelNode()
1692 .objectType("AppTreeLevelNode")
1693 .id("root")
1694 .root(true)
1695 .title("Layers")
1696 .childrenIds(List.of(
1697 "lyr:3dbag_utrecht:tiles3d",
1698 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1699 "lyr:3d_basisvoorziening_gebouwen_proxy:tiles3d",
1700 "lyr:3d_utrecht_proxied_auth:tiles3d")))
1701 .addLayerNodesItem(new AppTreeLayerNode()
1702 .objectType("AppTreeLayerNode")
1703 .id("lyr:3dbag_utrecht:tiles3d")
1704 .serviceId("3dbag_utrecht")
1705 .layerName("tiles3d")
1706 .visible(true))
1707 .addLayerNodesItem(new AppTreeLayerNode()
1708 .objectType("AppTreeLayerNode")
1709 .id("lyr:3d_basisvoorziening_gebouwen_proxy:tiles3d")
1710 .serviceId("3d_basisvoorziening_gebouwen_proxy")
1711 .layerName("tiles3d")
1712 .visible(false))
1713 .addLayerNodesItem(new AppTreeLayerNode()
1714 .objectType("AppTreeLayerNode")
1715 .id("lyr:3d_utrecht_proxied_auth:tiles3d")
1716 .serviceId("3d_utrecht_proxied_auth")
1717 .layerName("tiles3d")
1718 .visible(false))
1719 .addLayerNodesItem(new AppTreeLayerNode()
1720 .objectType("AppTreeLayerNode")
1721 .id("lyr:snapshot-geoserver:postgis:begroeidterreindeel")
1722 .serviceId("snapshot-geoserver")
1723 .layerName("postgis:begroeidterreindeel")
1724 .visible(true))
1725 .addTerrainLayerNodesItem(new AppTreeLevelNode()
1726 .objectType("AppTreeLevelNode")
1727 .id("root-terrain-layers")
1728 .root(true)
1729 .title("Terrain Layers")
1730 .childrenIds(List.of("lyr:ahn_terrain_model:quantizedmesh")))
1731 .addTerrainLayerNodesItem(new AppTreeLayerNode()
1732 .objectType("AppTreeLayerNode")
1733 .id("lyr:ahn_terrain_model:quantizedmesh")
1734 .serviceId("ahn_terrain_model")
1735 .layerName("quantizedmesh")
1736 .visible(false)));
1737
1738 applicationRepository.save(app);
1739
1740 app = new Application()
1741 .setName("public-with-auth")
1742 .setTitle("Public app with one restricted layer in a group")
1743 .setCrs("EPSG:28992")
1744 .setAuthorizationRules(ruleAnonymousRead)
1745 .setInitialExtent(
1746 new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d))
1747 .setMaxExtent(
1748 new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d))
1749 .setStyling(new AppStyling().logo(logo.getId().toString()))
1750 .setContentRoot(new AppContent()
1751 .addBaseLayerNodesItem(new AppTreeLevelNode()
1752 .objectType("AppTreeLevelNode")
1753 .id("root")
1754 .root(true)
1755 .title("Basemaps")
1756 .childrenIds(List.of("lyr:openbasiskaart:osm")))
1757 .addBaseLayerNodesItem(new AppTreeLayerNode()
1758 .objectType("AppTreeLayerNode")
1759 .id("lyr:openbasiskaart:osm")
1760 .serviceId("openbasiskaart")
1761 .layerName("osm")
1762 .visible(true))
1763 .addLayerNodesItem(new AppTreeLevelNode()
1764 .objectType("AppTreeLevelNode")
1765 .id("root")
1766 .root(true)
1767 .title("Application layers")
1768 .childrenIds(List.of(
1769 "lyr:snapshot-geoserver:postgis:kadastraal_perceel", "xpfhl34VmghkU12nP9Jer")))
1770 .addLayerNodesItem(new AppTreeLayerNode()
1771 .objectType("AppTreeLayerNode")
1772 .id("lyr:snapshot-geoserver:postgis:kadastraal_perceel")
1773 .serviceId("snapshot-geoserver")
1774 .layerName("postgis:kadastraal_perceel")
1775 .visible(true))
1776 .addLayerNodesItem(new AppTreeLevelNode()
1777 .id("xpfhl34VmghkU12nP9Jer")
1778 .root(false)
1779 .title("restricted")
1780 .objectType("AppTreeLevelNode")
1781 .childrenIds(List.of("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1782 .addLayerNodesItem(new AppTreeLayerNode()
1783 .objectType("AppTreeLayerNode")
1784 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1785 .visible(true)
1786 .serviceId("filtered-snapshot-geoserver")
1787 .layerName("postgis:begroeidterreindeel")))
1788 .setSettings(new AppSettings());
1789
1790 applicationRepository.save(app);
1791
1792 Configuration config = new Configuration();
1793 config.setKey(Configuration.DEFAULT_APP);
1794 config.setValue("default");
1795 configurationRepository.save(config);
1796 config = new Configuration();
1797 config.setKey(Configuration.DEFAULT_BASE_APP);
1798 config.setValue("base");
1799 configurationRepository.save(config);
1800 }
1801
1802 private void createConfigurationTestData() throws JacksonException {
1803 Configuration config = new Configuration();
1804 config.setKey("test");
1805 config.setAvailableForViewer(true);
1806 config.setValue("test value");
1807 config.setJsonValue(new JsonMapper().readTree("{ \"someProperty\": 1, \"nestedObject\": { \"num\": 42 } }"));
1808 configurationRepository.save(config);
1809 }
1810
1811 @Transactional
1812 public void createSolrIndex() throws Exception {
1813 if (connectToSpatialDbs) {
1814
1815
1816 featureSourceRepository.flush();
1817
1818 logger.info("Creating Solr index");
1819 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
1820 final String solrUrl = "http://" + (connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "solr") + ":8983/solr/";
1821 this.solrService.setSolrUrl(solrUrl);
1822 SolrHelper solrHelper = new SolrHelper(this.solrService.getSolrClientForIndexing())
1823 .withBatchSize(solrBatchSize)
1824 .withGeometryValidationRule(solrGeometryValidationRule);
1825 GeoService geoService =
1826 geoServiceRepository.findById("snapshot-geoserver").orElseThrow();
1827 Application defaultApp = applicationRepository.findByName("default");
1828
1829 TMFeatureType begroeidterreindeelFT = geoService.findFeatureTypeForLayer(
1830 geoService.findLayer("postgis:begroeidterreindeel"), featureSourceRepository);
1831
1832 TMFeatureType wegdeelFT = geoService.findFeatureTypeForLayer(
1833 geoService.findLayer("sqlserver:wegdeel"), featureSourceRepository);
1834
1835 TMFeatureType kadastraalPerceelFT = geoService.findFeatureTypeForLayer(
1836 geoService.findLayer("postgis:kadastraal_perceel"), featureSourceRepository);
1837
1838 try (solrHelper) {
1839 SearchIndex begroeidterreindeelIndex = null;
1840 if (begroeidterreindeelFT != null) {
1841 begroeidterreindeelIndex = new SearchIndex()
1842 .setName("Begroeidterreindeel")
1843 .setFeatureTypeId(begroeidterreindeelFT.getId())
1844 .setSearchFieldsUsed(List.of("class", "plus_fysiekvoorkomen", "bronhouder"))
1845 .setSearchDisplayFieldsUsed(List.of("class", "plus_fysiekvoorkomen"));
1846 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1847 begroeidterreindeelIndex = solrHelper.addFeatureTypeIndex(
1848 begroeidterreindeelIndex,
1849 begroeidterreindeelFT,
1850 featureSourceFactoryHelper,
1851 searchIndexRepository);
1852 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1853 }
1854
1855 SearchIndex kadastraalPerceelIndex = null;
1856 if (kadastraalPerceelFT != null) {
1857 kadastraalPerceelIndex = new SearchIndex()
1858 .setName("kadastraal_perceel")
1859 .setFeatureTypeId(kadastraalPerceelFT.getId())
1860 .setSearchFieldsUsed(List.of("aanduiding"))
1861 .setSearchDisplayFieldsUsed(List.of("aanduiding"));
1862 kadastraalPerceelIndex = searchIndexRepository.save(kadastraalPerceelIndex);
1863 kadastraalPerceelIndex = solrHelper.addFeatureTypeIndex(
1864 kadastraalPerceelIndex,
1865 kadastraalPerceelFT,
1866 featureSourceFactoryHelper,
1867 searchIndexRepository);
1868 kadastraalPerceelIndex = searchIndexRepository.save(kadastraalPerceelIndex);
1869 }
1870
1871 SearchIndex wegdeelIndex = null;
1872 if (wegdeelFT != null) {
1873 wegdeelIndex = new SearchIndex()
1874 .setName("Wegdeel")
1875 .setFeatureTypeId(wegdeelFT.getId())
1876 .setSearchFieldsUsed(List.of(
1877 "function_", "plus_fysiekvoorkomenwegdeel", "surfacematerial", "bronhouder"))
1878 .setSearchDisplayFieldsUsed(List.of("function_", "plus_fysiekvoorkomenwegdeel"));
1879 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1880 wegdeelIndex = solrHelper.addFeatureTypeIndex(
1881 wegdeelIndex, wegdeelFT, featureSourceFactoryHelper, searchIndexRepository);
1882 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1883 }
1884
1885 featureSourceRepository
1886 .getByTitle("PostGIS")
1887 .flatMap(fs -> fs.getFeatureTypes().stream()
1888 .filter(ft -> ft.getName().equals("bak"))
1889 .findFirst())
1890 .ifPresent(ft -> {
1891 SearchIndex bak = new SearchIndex()
1892 .setName("bak")
1893 .setFeatureTypeId(ft.getId())
1894 .setSearchFieldsUsed(List.of("gmlid", "identificatie", "plus_type"))
1895 .setSearchDisplayFieldsUsed(List.of("gmlid", "plus_type", "bronhouder"));
1896 searchIndexRepository.save(bak);
1897 try {
1898 bak = solrHelper.addFeatureTypeIndex(
1899 bak, ft, featureSourceFactoryHelper, searchIndexRepository);
1900 searchIndexRepository.save(bak);
1901 } catch (IOException | SolrServerException e) {
1902 throw new RuntimeException(e);
1903 }
1904 });
1905
1906
1907
1908 featureSourceRepository
1909 .getByTitle("PostGIS OSM")
1910 .flatMap(fs -> fs.getFeatureTypes().stream()
1911 .filter(ft -> ft.getName().equals("osm_roads"))
1912 .findFirst())
1913 .ifPresent(ft -> {
1914 SearchIndex osm_no_pk = new SearchIndex()
1915 .setName("osm_no_pk")
1916 .setFeatureTypeId(ft.getId())
1917 .setSearchFieldsUsed(List.of("landuse", "osm_id", "natural", "boundary"))
1918 .setSearchDisplayFieldsUsed(
1919 List.of("landuse", "osm_id", "natural", "amenity", "boundary"));
1920 searchIndexRepository.save(osm_no_pk);
1921 });
1922
1923 AppTreeLayerNode begroeidTerreindeelLayerNode = defaultApp
1924 .getAllAppTreeLayerNode()
1925 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1926 .findFirst()
1927 .orElse(null);
1928
1929 if (begroeidTerreindeelLayerNode != null && begroeidterreindeelIndex != null) {
1930 defaultApp
1931 .getAppLayerSettings(begroeidTerreindeelLayerNode)
1932 .setSearchIndexId(begroeidterreindeelIndex.getId());
1933 }
1934
1935 AppTreeLayerNode kadastraalPerceelLayerNode = defaultApp
1936 .getAllAppTreeLayerNode()
1937 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:postgis:kadastraal_perceel"))
1938 .findFirst()
1939 .orElse(null);
1940
1941 if (kadastraalPerceelLayerNode != null && kadastraalPerceelIndex != null) {
1942 defaultApp
1943 .getAppLayerSettings(kadastraalPerceelLayerNode)
1944 .setSearchIndexId(kadastraalPerceelIndex.getId());
1945 }
1946
1947 AppTreeLayerNode wegdeel = defaultApp
1948 .getAllAppTreeLayerNode()
1949 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:sqlserver:wegdeel"))
1950 .findFirst()
1951 .orElse(null);
1952
1953 if (wegdeel != null && wegdeelIndex != null) {
1954 defaultApp.getAppLayerSettings(wegdeel).setSearchIndexId(wegdeelIndex.getId());
1955 }
1956
1957 applicationRepository.save(defaultApp);
1958 }
1959 }
1960 }
1961
1962 private void createSearchIndexTasks() {
1963 logger.info("Creating search index tasks");
1964 List.of("Begroeidterreindeel", "kadastraal_perceel")
1965 .forEach(name -> searchIndexRepository.findByName(name).ifPresent(index -> {
1966 index.setSchedule(new TaskSchedule()
1967
1968 .cronExpression("0 0 0/1 1/1 * ? *")
1969
1970
1971 .description("Update Solr index \"" + name + "\" every hour"));
1972 try {
1973 final UUID uuid = taskManagerService.createTask(
1974 IndexTask.class,
1975 new TMJobDataMap(Map.of(
1976 Task.TYPE_KEY,
1977 TaskType.INDEX,
1978 Task.DESCRIPTION_KEY,
1979 index.getSchedule().getDescription(),
1980 IndexTask.INDEX_KEY,
1981 index.getId().toString(),
1982 Task.PRIORITY_KEY,
1983 10)),
1984 index.getSchedule().getCronExpression());
1985
1986 index.getSchedule().setUuid(uuid);
1987 searchIndexRepository.save(index);
1988
1989 logger.info("Created task to update Solr index with key: {}", uuid);
1990 } catch (SchedulerException e) {
1991 logger.error("Error creating scheduled solr index task", e);
1992 }
1993 }));
1994 }
1995
1996 private void createPages() throws IOException {
1997 Upload logo = new Upload()
1998 .setCategory(Upload.CATEGORY_PORTAL_IMAGE)
1999 .setFilename("gradient.svg")
2000 .setMimeType("image/svg+xml")
2001 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
2002 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2003 uploadRepository.save(logo);
2004
2005 Page loggedIn = new Page();
2006 loggedIn.setAuthorizationRules(ruleLoggedIn);
2007 loggedIn.setName("loggedIn");
2008 loggedIn.setType("page");
2009 loggedIn.setContent("About Tailormap");
2010 loggedIn.setContent("""
2011 # About Tailormap
2012 This is a page for logged in users.
2013 """);
2014 pageRepository.save(loggedIn);
2015
2016 Page about = new Page();
2017 about.setAuthorizationRules(ruleAnonymousRead);
2018 about.setName("about");
2019 about.setType("page");
2020 about.setContent("About Tailormap");
2021 about.setContent("""
2022 # About Tailormap
2023
2024 This is a page about *Tailormap*. It doesn't say much yet.
2025 """);
2026 pageRepository.save(about);
2027
2028 Page page = new Page();
2029 page.setAuthorizationRules(ruleAnonymousRead);
2030 page.setName("home");
2031 page.setType("page");
2032 page.setTitle("Tailormap - Home");
2033 page.setContent("""
2034 # Welcome to Tailormap!
2035
2036 This page is only visible when you implement a frontend to display pages, or get it (including a simple CMS)
2037 from [B3Partners](https:
2038 """);
2039 page.setClassName(null);
2040 page.setTiles(List.of(
2041 new PageTile()
2042 .authorizationRules(ruleAnonymousRead)
2043 .id(UUID.randomUUID().toString())
2044 .title("Default app")
2045 .tileType(PageTile.TileTypeEnum.APPLICATION)
2046 .applicationId(Optional.ofNullable(applicationRepository.findByName("default"))
2047 .map(Application::getId)
2048 .orElse(null))
2049 .image(logo.getId().toString())
2050 .content("*Default app* tile content")
2051 .filterRequireAuthorization(false)
2052 .openInNewWindow(false),
2053 new PageTile()
2054 .authorizationRules(ruleAnonymousRead)
2055 .id(UUID.randomUUID().toString())
2056 .title("Secured app")
2057 .tileType(PageTile.TileTypeEnum.APPLICATION)
2058 .applicationId(Optional.ofNullable(applicationRepository.findByName("secured"))
2059 .map(Application::getId)
2060 .orElse(null))
2061 .filterRequireAuthorization(true)
2062 .content("Secure app, only shown if user has authorization")
2063 .openInNewWindow(false),
2064 new PageTile()
2065 .authorizationRules(ruleAnonymousRead)
2066 .id(UUID.randomUUID().toString())
2067 .title("Secured app (unfiltered)")
2068 .tileType(PageTile.TileTypeEnum.APPLICATION)
2069 .applicationId(Optional.ofNullable(applicationRepository.findByName("secured"))
2070 .map(Application::getId)
2071 .orElse(null))
2072 .filterRequireAuthorization(false)
2073 .content("Secure app, tile shown to everyone")
2074 .openInNewWindow(false),
2075 new PageTile()
2076 .authorizationRules(ruleAnonymousRead)
2077 .id(UUID.randomUUID().toString())
2078 .title("About")
2079 .tileType(PageTile.TileTypeEnum.PAGE)
2080 .pageId(about.getId())
2081 .openInNewWindow(false),
2082 new PageTile()
2083 .authorizationRules(ruleAnonymousRead)
2084 .id(UUID.randomUUID().toString())
2085 .title("B3Partners")
2086 .tileType(PageTile.TileTypeEnum.URL)
2087 .url("https://www.b3partners.nl/")
2088 .openInNewWindow(true),
2089 new PageTile()
2090 .authorizationRules(ruleLoggedIn)
2091 .id(UUID.randomUUID().toString())
2092 .title("Github repository")
2093 .tileType(PageTile.TileTypeEnum.URL)
2094 .url("https://github.com/Tailormap/tailormap-viewer")
2095 .openInNewWindow(true),
2096 new PageTile()
2097 .authorizationRules(ruleAnonymousRead)
2098 .id(UUID.randomUUID().toString())
2099 .tileType(PageTile.TileTypeEnum.PAGE)
2100 .title("Secured page")
2101 .pageId(loggedIn.getId())
2102 .filterRequireAuthorization(true)
2103 .openInNewWindow(true),
2104 new PageTile()
2105 .authorizationRules(ruleAnonymousRead)
2106 .id(UUID.randomUUID().toString())
2107 .tileType(PageTile.TileTypeEnum.PAGE)
2108 .title("Secured page (unfiltered)")
2109 .pageId(loggedIn.getId())
2110 .filterRequireAuthorization(false)
2111 .openInNewWindow(true)));
2112 pageRepository.save(page);
2113
2114 Configuration c = new Configuration();
2115 c.setKey(HOME_PAGE);
2116 c.setValue(page.getId().toString());
2117 configurationRepository.save(c);
2118
2119 List<MenuItem> globalMenuItems = List.of(
2120 new MenuItem().pageId(about.getId()).label("About").openInNewWindow(false),
2121 new MenuItem()
2122 .label("B3Partners website")
2123 .url("https://www.b3partners.nl/")
2124 .openInNewWindow(true)
2125 .exclusiveOnPageId(about.getId()));
2126 c = new Configuration();
2127 c.setKey(PORTAL_MENU);
2128 c.setJsonValue(new JsonMapper().valueToTree(globalMenuItems));
2129 configurationRepository.save(c);
2130 }
2131
2132 private void insertTestDrawing() {
2133
2134 try {
2135 this.jdbcClient.sql("""
2136 INSERT INTO data.drawing (id,name,description,domain_data,"access",created_by,created_at,updated_by,updated_at,srid,"version") VALUES
2137 ('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);
2138 """).update();
2139
2140 this.jdbcClient.sql("""
2141 INSERT INTO data.drawing_feature (drawing_id,id,geometry,properties) VALUES
2142 ('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),
2143 ('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),
2144 ('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)
2145 """).update();
2146 } catch (Exception any) {
2147 logger.error("Error inserting test drawing in data schema, some tests may fail", any);
2148 }
2149 }
2150
2151 private void insertDrawingStyle() throws IOException {
2152 Upload upload = new Upload()
2153 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2154 .setMimeType("image/svg+xml")
2155 .setFilename("ISO_7001_PI_PF_007.svg")
2156 .setContent(new ClassPathResource("test/ISO_7001_PI_PF_007.svg").getContentAsByteArray())
2157 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2158 upload = uploadRepository.save(upload);
2159 UUID drinkwaterImageId = upload.getId();
2160
2161 upload = new Upload()
2162 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2163 .setMimeType("image/svg+xml")
2164 .setFilename("lichtpunt.svg")
2165 .setContent(new ClassPathResource("test/lichtpunt.svg").getContentAsByteArray())
2166 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2167 upload = uploadRepository.save(upload);
2168 UUID lichtpuntImageId = upload.getId();
2169
2170 upload = new Upload()
2171 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2172 .setMimeType("image/svg+xml")
2173 .setFilename("ISO_7010_E003_-_First_aid_sign.svg")
2174 .setContent(new ClassPathResource("test/ISO_7010_E003_-_First_aid_sign.svg").getContentAsByteArray())
2175 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2176 upload = uploadRepository.save(upload);
2177 UUID firstAidImageId = upload.getId();
2178
2179 Properties props = new Properties();
2180 props.putAll(Map.of(
2181 "water-uuid", drinkwaterImageId.toString(),
2182 "lichtpunt-uuid", lichtpuntImageId.toString(),
2183 "first-aid-uuid", firstAidImageId.toString()));
2184
2185 String drawingStyles =
2186 new ClassPathResource("test/object-drawing-style.json").getContentAsString(StandardCharsets.UTF_8);
2187 drawingStyles = new PropertyPlaceholderHelper("${", "}").replacePlaceholders(drawingStyles, props);
2188
2189
2190 upload = new Upload()
2191 .setCategory(Upload.CATEGORY_DRAWING_STYLE)
2192 .setMimeType("application/json")
2193 .setFilename("object-drawing-style.json")
2194 .setContent(drawingStyles.getBytes(StandardCharsets.UTF_8))
2195 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2196 uploadRepository.save(upload);
2197 }
2198 }