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