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