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