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