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 CatalogNode wfsFeatureSourceCatalogNode =
666 new CatalogNode().id("wfs_feature_sources").title("WFS feature sources");
667 rootCatalogNode.addChildrenItem(wfsFeatureSourceCatalogNode.getId());
668 catalog.getNodes().add(wfsFeatureSourceCatalogNode);
669
670 services.stream().filter(s -> s.getProtocol() == WMS).forEach(s -> {
671 geoServiceHelper.findAndSaveRelatedWFS(s);
672 List<TMFeatureSource> linkedSources = featureSourceRepository.findByLinkedServiceId(s.getId());
673 for (TMFeatureSource linkedSource : linkedSources) {
674 wfsFeatureSourceCatalogNode.addItemsItem(new TailormapObjectRef()
675 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
676 .id(linkedSource.getId().toString()));
677 }
678 });
679
680 String geodataPassword = "980f1c8A-25933b2";
681
682 Map<String, TMFeatureSource> featureSources = Map.of(
683 "postgis",
684 new TMFeatureSource()
685 .setProtocol(TMFeatureSource.Protocol.JDBC)
686 .setTitle("PostGIS")
687 .setJdbcConnection(new JDBCConnectionProperties()
688 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
689 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
690 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
691 .database("geodata")
692 .schema("public")
693 .additionalProperties(Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
694 .setAuthentication(new ServiceAuthentication()
695 .method(ServiceAuthentication.MethodEnum.PASSWORD)
696 .username("geodata")
697 .password(geodataPassword)),
698 "postgis_osm",
699 new TMFeatureSource()
700 .setProtocol(TMFeatureSource.Protocol.JDBC)
701 .setTitle("PostGIS OSM")
702 .setJdbcConnection(new JDBCConnectionProperties()
703 .dbtype(JDBCConnectionProperties.DbtypeEnum.POSTGIS)
704 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "postgis")
705 .port(connectToSpatialDbsAtLocalhost ? 54322 : 5432)
706 .database("geodata")
707 .schema("osm")
708 .additionalProperties(Map.of("connectionOptions", "?ApplicationName=tailormap-api")))
709 .setAuthentication(new ServiceAuthentication()
710 .method(ServiceAuthentication.MethodEnum.PASSWORD)
711 .username("geodata")
712 .password(geodataPassword)),
713 "oracle",
714 new TMFeatureSource()
715 .setProtocol(TMFeatureSource.Protocol.JDBC)
716 .setTitle("Oracle")
717 .setJdbcConnection(new JDBCConnectionProperties()
718 .dbtype(JDBCConnectionProperties.DbtypeEnum.ORACLE)
719 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "oracle")
720 .database("/FREEPDB1")
721 .schema("GEODATA")
722 .additionalProperties(Map.of("connectionOptions", "?oracle.jdbc.J2EE13Compliant=true")))
723 .setAuthentication(new ServiceAuthentication()
724 .method(ServiceAuthentication.MethodEnum.PASSWORD)
725 .username("geodata")
726 .password(geodataPassword)),
727 "sqlserver",
728 new TMFeatureSource()
729 .setProtocol(TMFeatureSource.Protocol.JDBC)
730 .setTitle("MS SQL Server")
731 .setJdbcConnection(new JDBCConnectionProperties()
732 .dbtype(JDBCConnectionProperties.DbtypeEnum.SQLSERVER)
733 .host(connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "sqlserver")
734 .database("geodata")
735 .schema("dbo")
736 .additionalProperties(Map.of("connectionOptions", ";encrypt=false")))
737 .setAuthentication(new ServiceAuthentication()
738 .method(ServiceAuthentication.MethodEnum.PASSWORD)
739 .username("geodata")
740 .password(geodataPassword)),
741 "pdok-kadaster-bestuurlijkegebieden",
742 new TMFeatureSource()
743 .setProtocol(TMFeatureSource.Protocol.WFS)
744 .setUrl(
745 "https://service.pdok.nl/kadaster/brk-bestuurlijke-gebieden/wfs/v1_0?service=WFS&VERSION=2.0.0")
746 .setTitle("Bestuurlijke gebieden")
747 .setNotes(
748 "Overzicht van de bestuurlijke indeling van Nederland in gemeenten en provincies alsmede de rijksgrens. Gegevens zijn afgeleid uit de Basisregistratie Kadaster (BRK)."));
749 featureSourceRepository.saveAll(featureSources.values());
750
751 new WFSFeatureSourceHelper().loadCapabilities(featureSources.get("pdok-kadaster-bestuurlijkegebieden"));
752 geoServiceRepository.findById("pdok-kadaster-bestuurlijkegebieden").ifPresent(geoService -> {
753 geoService
754 .getSettings()
755 .getLayerSettings()
756 .put(
757 "Provinciegebied",
758 new GeoServiceLayerSettings()
759 .description("The administrative boundary of Dutch Provinces, connected to a WFS.")
760 .featureType(new FeatureTypeRef()
761 .featureSourceId(featureSources
762 .get("pdok-kadaster-bestuurlijkegebieden")
763 .getId())
764 .featureTypeName(PROVINCIE_FEATURE_TYPE_NAME))
765 .title("Provinciegebied (WFS)"));
766 geoServiceRepository.save(geoService);
767 });
768
769 geoServiceRepository.findById("bestuurlijkegebieden-proxied").ifPresent(geoService -> {
770 geoService
771 .getSettings()
772 .getLayerSettings()
773 .put(
774 "Provinciegebied",
775 new GeoServiceLayerSettings()
776 .featureType(new FeatureTypeRef()
777 .featureSourceId(featureSources
778 .get("pdok-kadaster-bestuurlijkegebieden")
779 .getId())
780 .featureTypeName(PROVINCIE_FEATURE_TYPE_NAME))
781 .title("Provinciegebied (WFS, proxied met auth)"));
782 geoServiceRepository.save(geoService);
783 });
784
785 CatalogNode featureSourceCatalogNode =
786 new CatalogNode().id("feature_sources").title("Test feature sources");
787 rootCatalogNode.addChildrenItem(featureSourceCatalogNode.getId());
788 catalog.getNodes().add(featureSourceCatalogNode);
789
790 for (TMFeatureSource featureSource : featureSources.values()) {
791 featureSourceCatalogNode.addItemsItem(new TailormapObjectRef()
792 .kind(TailormapObjectRef.KindEnum.FEATURE_SOURCE)
793 .id(featureSource.getId().toString()));
794 }
795 catalogRepository.save(catalog);
796
797 if (connectToSpatialDbs) {
798 featureSources.values().forEach(fs -> {
799 try {
800 if (fs.getProtocol() == TMFeatureSource.Protocol.JDBC) {
801 new JDBCFeatureSourceHelper().loadCapabilities(fs);
802 } else if (fs.getProtocol() == TMFeatureSource.Protocol.WFS) {
803 new WFSFeatureSourceHelper().loadCapabilities(fs);
804 }
805 } catch (Exception e) {
806 logger.error("Error loading capabilities for feature source {}", fs.getTitle(), e);
807 }
808 });
809
810 services.stream()
811
812
813 .filter(s -> s.getId().startsWith("snapshot-geoserver"))
814 .forEach(s -> s.getSettings()
815 .layerSettings(Map.of(
816 "postgis:begroeidterreindeel",
817 new GeoServiceLayerSettings()
818 .description("""
819 This layer shows data from https:
820
821 https:
822 .featureType(new FeatureTypeRef()
823 .featureSourceId(featureSources
824 .get("postgis")
825 .getId())
826 .featureTypeName("begroeidterreindeel")),
827 "postgis:bak",
828 new GeoServiceLayerSettings()
829 .featureType(new FeatureTypeRef()
830 .featureSourceId(featureSources
831 .get("postgis")
832 .getId())
833 .featureTypeName("bak")),
834 "postgis:kadastraal_perceel",
835 new GeoServiceLayerSettings()
836 .description("cadastral parcel label points")
837 .featureType(new FeatureTypeRef()
838 .featureSourceId(featureSources
839 .get("postgis")
840 .getId())
841 .featureTypeName("kadastraal_perceel")),
842 "sqlserver:wegdeel",
843 new GeoServiceLayerSettings()
844 .attribution(
845 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)")
846 .description("""
847 This layer shows data from [MS SQL Server](https:
848
849 https:
850 .featureType(new FeatureTypeRef()
851 .featureSourceId(featureSources
852 .get("sqlserver")
853 .getId())
854 .featureTypeName("wegdeel")),
855 "oracle:WATERDEEL",
856 new GeoServiceLayerSettings()
857 .description("This layer shows data from Oracle Spatial.")
858 .featureType(new FeatureTypeRef()
859 .featureSourceId(featureSources
860 .get("oracle")
861 .getId())
862 .featureTypeName("WATERDEEL")),
863 "postgis:osm_polygon",
864 new GeoServiceLayerSettings()
865 .description("This layer shows OSM data from postgis.")
866 .featureType(new FeatureTypeRef()
867 .featureSourceId(featureSources
868 .get("postgis_osm")
869 .getId())
870 .featureTypeName("osm_polygon")))));
871 }
872
873 featureSources.get("pdok-kadaster-bestuurlijkegebieden").getFeatureTypes().stream()
874 .filter(ft -> ft.getName().equals(PROVINCIE_FEATURE_TYPE_NAME))
875 .findFirst()
876 .ifPresent(ft -> {
877 ft.getSettings().addHideAttributesItem("identificatie");
878 ft.getSettings().addHideAttributesItem("ligtInLandCode");
879 ft.getSettings().addHideAttributesItem("fuuid");
880 ft.getSettings().putAttributeSettingsItem("naam", new AttributeSettings().title("Naam"));
881 ft.getSettings()
882 .setTemplate(new FeatureTypeTemplate()
883 .templateLanguage("simple")
884 .markupLanguage("markdown")
885 .template("""
886 ### Provincie
887 Deze provincie heet **{{naam}}** en ligt in _{{ligtInLandNaam}}_.
888
889 | Attribuut | Waarde |
890 | --------- | ------------------ |
891 | `code` | {{code}} |
892 | `naam` | {{naam}} |
893 | `ligt in` | {{ligtInLandNaam}} |"""));
894 });
895
896 featureSources.get("postgis").getFeatureTypes().stream()
897 .filter(ft -> ft.getName().equals("begroeidterreindeel"))
898 .findFirst()
899 .ifPresent(ft -> {
900 ft.getSettings().addHideAttributesItem("terminationdate");
901 ft.getSettings().addHideAttributesItem("geom_kruinlijn");
902 ft.getSettings().putAttributeSettingsItem("gmlid", new AttributeSettings().title("GML ID"));
903 ft.getSettings()
904 .putAttributeSettingsItem("identificatie", new AttributeSettings().title("Identificatie"));
905 ft.getSettings()
906 .putAttributeSettingsItem(
907 "tijdstipregistratie", new AttributeSettings().title("Registratie"));
908 ft.getSettings()
909 .putAttributeSettingsItem(
910 "eindregistratie", new AttributeSettings().title("Eind registratie"));
911 ft.getSettings().putAttributeSettingsItem("class", new AttributeSettings().title("Klasse"));
912 ft.getSettings()
913 .putAttributeSettingsItem("bronhouder", new AttributeSettings().title("Bronhouder"));
914 ft.getSettings()
915 .putAttributeSettingsItem("inonderzoek", new AttributeSettings().title("In onderzoek"));
916 ft.getSettings()
917 .putAttributeSettingsItem(
918 "relatievehoogteligging", new AttributeSettings().title("Relatieve hoogteligging"));
919 ft.getSettings()
920 .putAttributeSettingsItem("bgt_status", new AttributeSettings().title("BGT status"));
921 ft.getSettings()
922 .putAttributeSettingsItem("plus_status", new AttributeSettings().title("Plus-status"));
923 ft.getSettings()
924 .putAttributeSettingsItem(
925 "plus_fysiekvoorkomen", new AttributeSettings().title("Plus-fysiek voorkomen"));
926 ft.getSettings()
927 .putAttributeSettingsItem(
928 "begroeidterreindeeloptalud", new AttributeSettings().title("Op talud"));
929 ft.getSettings().addAttributeOrderItem("identificatie");
930 ft.getSettings().addAttributeOrderItem("bronhouder");
931 ft.getSettings().addAttributeOrderItem("class");
932 ft.getSettings()
933 .addAttachmentAttributesItem(new AttachmentAttributeType()
934 .attributeName("bijlage")
935 .maxAttachmentSize(4_000_000L)
936 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
937 try {
938 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
939 } catch (IOException | SQLException e) {
940 throw new RuntimeException("Failed to create attachments table", e);
941 }
942 ft.getSettings().addEditableAttributesItem("identificatie");
943 ft.getSettings().addEditableAttributesItem("bronhouder");
944 ft.getSettings().addEditableAttributesItem("class");
945 ft.getSettings().addEditableAttributesItem("gmlid");
946 ft.getSettings().addEditableAttributesItem("lv_publicatiedatum");
947 ft.getSettings().addEditableAttributesItem("creationdate");
948 ft.getSettings().addEditableAttributesItem("tijdstipregistratie");
949 ft.getSettings().addEditableAttributesItem("eindregistratie");
950 ft.getSettings().addEditableAttributesItem("terminationdate");
951 ft.getSettings().addEditableAttributesItem("inonderzoek");
952 ft.getSettings().addEditableAttributesItem("relatievehoogteligging");
953 ft.getSettings().addEditableAttributesItem("bgt_status");
954 ft.getSettings().addEditableAttributesItem("plus_status");
955 ft.getSettings().addEditableAttributesItem("plus_fysiekvoorkomen");
956 ft.getSettings().addEditableAttributesItem("begroeidterreindeeloptalud");
957 });
958
959 featureSources.get("postgis").getFeatureTypes().stream()
960 .filter(ft -> ft.getName().equals("bak"))
961 .findFirst()
962 .ifPresent(ft -> {
963 ft.getSettings().addHideAttributesItem("gmlid");
964 ft.getSettings().addHideAttributesItem("lv_publicatiedatum");
965 ft.getSettings().addHideAttributesItem("creationdate");
966 ft.getSettings().addHideAttributesItem("tijdstipregistratie");
967 ft.getSettings().addHideAttributesItem("eindregistratie");
968 ft.getSettings().addHideAttributesItem("terminationdate");
969 ft.getSettings().addHideAttributesItem("inonderzoek");
970 ft.getSettings().addHideAttributesItem("relatievehoogteligging");
971 ft.getSettings().addHideAttributesItem("bgt_status");
972 ft.getSettings().addHideAttributesItem("plus_status");
973 ft.getSettings().addHideAttributesItem("function_");
974 ft.getSettings().addHideAttributesItem("plus_type");
975 });
976
977 featureSources.get("oracle").getFeatureTypes().stream()
978 .filter(ft -> ft.getName().equals("WATERDEEL"))
979 .findFirst()
980 .ifPresent(ft -> {
981 ft.getSettings()
982 .addAttachmentAttributesItem(new AttachmentAttributeType()
983 .attributeName("bijlage")
984 .maxAttachmentSize(4_000_000L)
985 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
986 try {
987 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
988 } catch (IOException | SQLException e) {
989 throw new RuntimeException("Failed to create attachments table", e);
990 }
991 ft.getSettings().addEditableAttributesItem("GMLID");
992 ft.getSettings().addEditableAttributesItem("IDENTIFICATIE");
993 ft.getSettings().addEditableAttributesItem("LV_PUBLICATIEDATUM");
994 ft.getSettings().addEditableAttributesItem("CREATIONDATE");
995 ft.getSettings().addEditableAttributesItem("TIJDSTIPREGISTRATIE");
996 ft.getSettings().addEditableAttributesItem("EINDREGISTRATIE");
997 ft.getSettings().addEditableAttributesItem("TERMINATIONDATE");
998 ft.getSettings().addEditableAttributesItem("BRONHOUDER");
999 ft.getSettings().addEditableAttributesItem("INONDERZOEK");
1000 ft.getSettings().addEditableAttributesItem("RELATIEVEHOOGTELIGGING");
1001 ft.getSettings().addEditableAttributesItem("BGT_STATUS");
1002 ft.getSettings().addEditableAttributesItem("PLUS_STATUS");
1003 ft.getSettings().addEditableAttributesItem("CLASS");
1004 ft.getSettings().addEditableAttributesItem("PLUS_TYPE");
1005 });
1006 featureSources.get("sqlserver").getFeatureTypes().stream()
1007 .filter(ft -> ft.getName().equals("wegdeel"))
1008 .findFirst()
1009 .ifPresent(ft -> {
1010 ft.getSettings()
1011 .addAttachmentAttributesItem(new AttachmentAttributeType()
1012 .attributeName("bijlage")
1013 .maxAttachmentSize(4_000_000L)
1014 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
1015 try {
1016 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
1017 } catch (IOException | SQLException e) {
1018 throw new RuntimeException("Failed to create attachments table", e);
1019 }
1020 ft.getSettings().addEditableAttributesItem("gmlid");
1021 ft.getSettings().addEditableAttributesItem("identificatie");
1022 ft.getSettings().addEditableAttributesItem("lv_publicatiedatum");
1023 ft.getSettings().addEditableAttributesItem("creationdate");
1024 ft.getSettings().addEditableAttributesItem("tijdstipregistratie");
1025 ft.getSettings().addEditableAttributesItem("eindregistratie");
1026 ft.getSettings().addEditableAttributesItem("terminationdate");
1027 ft.getSettings().addEditableAttributesItem("bronhouder");
1028 ft.getSettings().addEditableAttributesItem("inonderzoek");
1029 ft.getSettings().addEditableAttributesItem("relatievehoogteligging");
1030 ft.getSettings().addEditableAttributesItem("bgt_status");
1031 ft.getSettings().addEditableAttributesItem("plus_status");
1032 ft.getSettings().addEditableAttributesItem("function_");
1033 ft.getSettings().addEditableAttributesItem("plus_functiewegdeel");
1034 ft.getSettings().addEditableAttributesItem("plus_fysiekvoorkomenwegdeel");
1035 ft.getSettings().addEditableAttributesItem("surfacematerial");
1036 ft.getSettings().addEditableAttributesItem("wegdeeloptalud");
1037 });
1038
1039 featureSources.get("postgis").getFeatureTypes().stream()
1040 .filter(ft -> ft.getName().equals("kadastraal_perceel"))
1041 .findFirst()
1042 .ifPresent(ft -> {
1043 ft.getSettings()
1044 .addAttachmentAttributesItem(new AttachmentAttributeType()
1045 .attributeName("bijlage")
1046 .maxAttachmentSize(4_000_000L)
1047 .mimeType("image/jpeg, image/svg+xml, .png, image/*"));
1048 try {
1049 AttachmentsHelper.createAttachmentTableForFeatureType(ft);
1050 } catch (IOException | SQLException e) {
1051 throw new RuntimeException("Failed to create attachments table for kadastraal_perceel", e);
1052 }
1053
1054 ft.getSettings().addHideAttributesItem("gml_id");
1055
1056 ft.getSettings().addEditableAttributesItem("begin_geldigheid");
1057 ft.getSettings().addEditableAttributesItem("tijdstip_registratie");
1058 });
1059
1060 featureSources.get("postgis_osm").getFeatureTypes().stream()
1061 .filter(ft -> ft.getName().equals("osm_polygon"))
1062 .findFirst()
1063 .ifPresent(ft -> {
1064 ft.getSettings().addEditableAttributesItem("osm_id");
1065 ft.getSettings().addEditableAttributesItem("building");
1066 ft.getSettings().addEditableAttributesItem("z_order");
1067 });
1068 }
1069
1070 public void createAppTestData() throws Exception {
1071 Upload logo = new Upload()
1072 .setCategory(Upload.CATEGORY_APP_LOGO)
1073 .setFilename("gradient.svg")
1074 .setMimeType("image/svg+xml")
1075 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
1076 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
1077 uploadRepository.save(logo);
1078
1079 List<AppTreeNode> baseNodes = List.of(
1080 new AppTreeLayerNode()
1081 .objectType("AppTreeLayerNode")
1082 .id("lyr:openbasiskaart:osm")
1083 .serviceId("openbasiskaart")
1084 .layerName("osm")
1085 .visible(true),
1086 new AppTreeLayerNode()
1087 .objectType("AppTreeLayerNode")
1088 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR")
1089 .serviceId("pdok-hwh-luchtfotorgb")
1090 .layerName("Actueel_orthoHR")
1091 .visible(false));
1092
1093 Application app = new Application()
1094 .setName("default")
1095 .setTitle("Tailormap demo")
1096 .setCrs("EPSG:28992")
1097 .setAuthorizationRules(ruleAnonymousRead)
1098 .setComponents(List.of(
1099 new Component()
1100 .type("SIMPLE_SEARCH")
1101 .config(new ComponentConfig()
1102 .enabled(true)
1103 .putAdditionalProperty("municipalities", List.of("0344"))),
1104 new Component().type("EDIT").config(new ComponentConfig().enabled(true)),
1105 new Component()
1106 .type("TOC")
1107 .config(new ComponentConfig()
1108 .enabled(true)
1109 .putAdditionalProperty("showEditLayerIcon", true)),
1110 new Component()
1111 .type("COORDINATE_LINK_WINDOW")
1112 .config(new ComponentConfig()
1113 .enabled(true)
1114 .putAdditionalProperty(
1115 "urls",
1116 List.of(
1117 Map.of(
1118 "id",
1119 "google-maps",
1120 "url",
1121 "https://www.google.com/maps/@[lat],[lon],18z",
1122 "alias",
1123 "Google Maps",
1124 "projection",
1125 "EPSG:4326"),
1126 Map.of(
1127 "id",
1128 "tm-demo",
1129 "url",
1130 "https://demo.tailormap.com/#@[X],[Y],18",
1131 "alias",
1132 "Tailormap demo",
1133 "projection",
1134 "EPSG:28992"))))))
1135 .setContentRoot(new AppContent()
1136 .addBaseLayerNodesItem(new AppTreeLevelNode()
1137 .objectType("AppTreeLevelNode")
1138 .id("root-base-layers")
1139 .root(true)
1140 .title("Base layers")
1141 .childrenIds(List.of(
1142 "lyr:openbasiskaart:osm",
1143 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1144 "lyr:openbasiskaart-proxied:osm",
1145 "lyr:openbasiskaart-tms:xyz",
1146 "lyr:b3p-mapproxy-luchtfoto:xyz")))
1147 .addBaseLayerNodesItem(
1148
1149
1150 new AppTreeLayerNode()
1151 .objectType("AppTreeLayerNode")
1152 .id("lyr:openbasiskaart-proxied:osm")
1153 .serviceId("openbasiskaart-proxied")
1154 .layerName("osm")
1155 .visible(false))
1156 .addBaseLayerNodesItem(new AppTreeLayerNode()
1157 .objectType("AppTreeLayerNode")
1158 .id("lyr:openbasiskaart-tms:xyz")
1159 .serviceId("openbasiskaart-tms")
1160 .layerName("xyz")
1161 .visible(false))
1162 .addBaseLayerNodesItem(new AppTreeLayerNode()
1163 .objectType("AppTreeLayerNode")
1164 .id("lyr:b3p-mapproxy-luchtfoto:xyz")
1165 .serviceId("b3p-mapproxy-luchtfoto")
1166 .layerName("xyz")
1167 .visible(false))
1168 .addLayerNodesItem(new AppTreeLevelNode()
1169 .objectType("AppTreeLevelNode")
1170 .id("root")
1171 .root(true)
1172 .title("Layers")
1173 .childrenIds(List.of(
1174 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1175 "lyr:bestuurlijkegebieden-proxied:Provinciegebied",
1176 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1177 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1178 "lyr:snapshot-geoserver:postgis:bak",
1179 "lyr:snapshot-geoserver:postgis:kadastraal_perceel",
1180 "lyr:snapshot-geoserver:sqlserver:wegdeel",
1181 "lyr:snapshot-geoserver:oracle:WATERDEEL",
1182 "lyr:snapshot-geoserver:BGT",
1183 "lvl:proxied",
1184 "lvl:osm",
1185 "lvl:archeo")))
1186 .addLayerNodesItem(new AppTreeLayerNode()
1187 .objectType("AppTreeLayerNode")
1188 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1189 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1190 .layerName("Provinciegebied")
1191 .visible(true))
1192
1193
1194
1195 .addLayerNodesItem(new AppTreeLayerNode()
1196 .objectType("AppTreeLayerNode")
1197 .id("lyr:bestuurlijkegebieden-proxied:Provinciegebied")
1198 .serviceId("bestuurlijkegebieden-proxied")
1199 .layerName("Provinciegebied")
1200 .visible(false))
1201 .addLayerNodesItem(new AppTreeLayerNode()
1202 .objectType("AppTreeLayerNode")
1203 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1204 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1205 .layerName("Gemeentegebied")
1206 .visible(true))
1207 .addLayerNodesItem(new AppTreeLayerNode()
1208 .objectType("AppTreeLayerNode")
1209 .id("lyr:snapshot-geoserver:postgis:begroeidterreindeel")
1210 .serviceId("snapshot-geoserver")
1211 .layerName("postgis:begroeidterreindeel")
1212 .visible(true))
1213 .addLayerNodesItem(new AppTreeLayerNode()
1214 .objectType("AppTreeLayerNode")
1215 .id("lyr:snapshot-geoserver:postgis:bak")
1216 .serviceId("snapshot-geoserver")
1217 .layerName("postgis:bak")
1218 .visible(false))
1219 .addLayerNodesItem(new AppTreeLayerNode()
1220 .objectType("AppTreeLayerNode")
1221 .id("lyr:snapshot-geoserver:postgis:kadastraal_perceel")
1222 .serviceId("snapshot-geoserver")
1223 .layerName("postgis:kadastraal_perceel")
1224 .visible(false))
1225 .addLayerNodesItem(new AppTreeLayerNode()
1226 .objectType("AppTreeLayerNode")
1227 .id("lyr:snapshot-geoserver:sqlserver:wegdeel")
1228 .serviceId("snapshot-geoserver")
1229 .layerName("sqlserver:wegdeel")
1230 .visible(true))
1231 .addLayerNodesItem(new AppTreeLayerNode()
1232 .objectType("AppTreeLayerNode")
1233 .id("lyr:snapshot-geoserver:oracle:WATERDEEL")
1234 .serviceId("snapshot-geoserver")
1235 .layerName("oracle:WATERDEEL")
1236 .visible(true))
1237 .addLayerNodesItem(new AppTreeLayerNode()
1238 .objectType("AppTreeLayerNode")
1239 .id("lyr:snapshot-geoserver:BGT")
1240 .serviceId("snapshot-geoserver")
1241 .layerName("BGT")
1242 .visible(false))
1243 .addLayerNodesItem(new AppTreeLevelNode()
1244 .objectType("AppTreeLevelNode")
1245 .id("lvl:proxied")
1246 .title("Proxied")
1247 .childrenIds(List.of("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1248 .addLayerNodesItem(new AppTreeLayerNode()
1249 .objectType("AppTreeLayerNode")
1250 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1251 .serviceId("snapshot-geoserver-proxied")
1252 .layerName("postgis:begroeidterreindeel")
1253 .visible(false))
1254 .addLayerNodesItem(new AppTreeLevelNode()
1255 .objectType("AppTreeLevelNode")
1256 .id("lvl:osm")
1257 .title("OSM")
1258 .childrenIds(List.of("lyr:snapshot-geoserver:postgis:osm_polygon")))
1259 .addLayerNodesItem(new AppTreeLayerNode()
1260 .objectType("AppTreeLayerNode")
1261 .id("lyr:snapshot-geoserver:postgis:osm_polygon")
1262 .serviceId("snapshot-geoserver")
1263 .layerName("postgis:osm_polygon")
1264 .visible(false))
1265 .addLayerNodesItem(new AppTreeLevelNode()
1266 .objectType("AppTreeLevelNode")
1267 .id("lvl:archeo")
1268 .title("Archeology")
1269 .childrenIds(List.of("lyr:demo:geomorfologie")))
1270 .addLayerNodesItem(new AppTreeLayerNode()
1271 .objectType("AppTreeLayerNode")
1272 .id("lyr:demo:geomorfologie")
1273 .serviceId("demo")
1274 .layerName("geomorfologie")
1275 .visible(true)))
1276 .setStyling(new AppStyling().logo(logo.getId().toString()))
1277 .setSettings(new AppSettings()
1278 .putLayerSettingsItem("lyr:openbasiskaart:osm", new AppLayerSettings().title("Openbasiskaart"))
1279 .putLayerSettingsItem(
1280 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR", new AppLayerSettings().title("Luchtfoto"))
1281 .putLayerSettingsItem(
1282 "lyr:openbasiskaart-proxied:osm",
1283 new AppLayerSettings().title("Openbasiskaart (proxied)"))
1284 .putLayerSettingsItem(
1285 "lyr:snapshot-geoserver:oracle:WATERDEEL",
1286 new AppLayerSettings()
1287 .opacity(50)
1288 .title("Waterdeel overridden title")
1289 .editable(true)
1290 .description("This is the layer description from the app layer setting.")
1291 .attribution(
1292 "CC BY 4.0 [BGT/Kadaster](https://www.nationaalgeoregister.nl/geonetwork/srv/api/records/2cb4769c-b56e-48fa-8685-c48f61b9a319)"))
1293 .putLayerSettingsItem(
1294 "lyr:snapshot-geoserver:postgis:osm_polygon",
1295 new AppLayerSettings()
1296 .description("OpenStreetMap polygon data in EPSG:3857")
1297 .opacity(60)
1298 .editable(true)
1299 .title("OSM Polygon (EPSG:3857)")
1300 .attribution(
1301 "© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors"))
1302 .putLayerSettingsItem(
1303 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1304 new AppLayerSettings()
1305 .editable(true)
1306 .addHideAttributesItem("begroeidterreindeeloptalud")
1307 .addReadOnlyAttributesItem("eindregistratie")
1308 .selectedStyles(
1309 List.of(
1310 new WMSStyle()
1311 .name("begroeidterreindeel")
1312 .title("Visualisatie van de begroeide terreindelen")
1313 .abstractText(
1314 "Deze stylesheet bevat de regels voor de visualisatie van het objecttype Begroeid Terreindeel")
1315 .legendUrl(
1316 URI.create(
1317 "https://snapshot.tailormap.nl/geoserver/ows?service=WMS&version=1.3.0&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer=test%3Apostgis_begroeidterreindeel")),
1318 new WMSStyle()
1319 .name("purple_polygon")
1320 .title("purple_polygon")
1321 .abstractText(null)
1322 .legendUrl(
1323 URI.create(
1324 "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")))))
1325 .putLayerSettingsItem(
1326 "lyr:snapshot-geoserver:postgis:kadastraal_perceel",
1327 new AppLayerSettings().editable(true).addReadOnlyAttributesItem("aanduiding"))
1328 .putLayerSettingsItem(
1329 "lyr:snapshot-geoserver:sqlserver:wegdeel", new AppLayerSettings().editable(true))
1330 .putLayerSettingsItem(
1331 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel",
1332 new AppLayerSettings().editable(false))
1333 .putLayerSettingsItem(
1334 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1335 new AppLayerSettings()
1336 .hiddenFunctionality(Set.of(FEATURE_INFO, ATTRIBUTE_LIST, EXPORT)))
1337 .addFilterGroupsItem(new FilterGroup()
1338 .id("filtergroup1")
1339 .source("PRESET")
1340 .type(FilterGroup.TypeEnum.ATTRIBUTE)
1341 .layerIds(List.of("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1342 .operator(FilterGroup.OperatorEnum.AND)
1343 .addFiltersItem(new Filter()
1344 .id("filter1")
1345 .type(Filter.TypeEnum.ATTRIBUTE)
1346 .condition(Filter.ConditionEnum.BEFORE)
1347 .addValueItem("2025-06-05")
1348 .attribute("creationdate")
1349 .attributeType(Filter.AttributeTypeEnum.DATE))
1350 .addFiltersItem(new Filter()
1351 .id("filter2")
1352 .type(Filter.TypeEnum.ATTRIBUTE)
1353 .condition(Filter.ConditionEnum.UNIQUE_VALUES)
1354 .addValueItem("bodembedekkers")
1355 .addValueItem("bosplantsoen")
1356 .addValueItem("gras- en kruidachtigen")
1357 .attribute("plus_fysiekvoorkomen")
1358 .attributeType(Filter.AttributeTypeEnum.STRING)
1359 .editConfiguration(new FilterEditConfiguration()
1360 .filterTool(FilterEditConfiguration.FilterToolEnum.CHECKBOX)
1361 .attributeValuesSettings(List.of(
1362 new AttributeValueSettings()
1363 .value("bodembedekkers")
1364 .initiallySelected(true)
1365 .selectable(true)
1366 .alias("Bodembedekkers"),
1367 new AttributeValueSettings()
1368 .value("bosplantsoen")
1369 .initiallySelected(true)
1370 .selectable(true)
1371 .alias("Bosplantsoen"),
1372 new AttributeValueSettings()
1373 .value("gras- en kruidachtigen")
1374 .initiallySelected(true)
1375 .selectable(true)
1376 .alias("Gras- en kruidachtigen"),
1377 new AttributeValueSettings()
1378 .value("griend en hakhout")
1379 .initiallySelected(false)
1380 .selectable(true),
1381 new AttributeValueSettings()
1382 .value("heesters")
1383 .initiallySelected(false)
1384 .selectable(true),
1385 new AttributeValueSettings()
1386 .value("planten")
1387 .initiallySelected(false)
1388 .selectable(true),
1389 new AttributeValueSettings()
1390 .value("struikrozen")
1391 .initiallySelected(false)
1392 .selectable(true),
1393 new AttributeValueSettings()
1394 .value("waardeOnbekend")
1395 .initiallySelected(false)
1396 .selectable(true))))))
1397 .addFilterGroupsItem(new FilterGroup()
1398 .id("filtergroup2")
1399 .source("PRESET")
1400 .type(FilterGroup.TypeEnum.ATTRIBUTE)
1401 .layerIds(List.of("lyr:snapshot-geoserver:postgis:kadastraal_perceel"))
1402 .operator(FilterGroup.OperatorEnum.AND)
1403 .addFiltersItem(new Filter()
1404 .id("filter3")
1405 .type(Filter.TypeEnum.ATTRIBUTE)
1406 .condition(Filter.ConditionEnum.u)
1407 .addValueItem("1")
1408 .addValueItem("12419")
1409 .attribute("perceelnummer")
1410 .attributeType(Filter.AttributeTypeEnum.DOUBLE)
1411 .editConfiguration(new FilterEditConfiguration()
1412 .filterTool(FilterEditConfiguration.FilterToolEnum.SLIDER)
1413 .initialLowerValue(1d)
1414 .initialUpperValue(12419d)
1415 .minimumValue(1d)
1416 .maximumValue(12419d)))));
1417
1418 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1419 app.setInitialExtent(
1420 new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d));
1421 app.setMaxExtent(new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d));
1422
1423 if (map5url != null) {
1424 AppTreeLevelNode root =
1425 (AppTreeLevelNode) app.getContentRoot().getBaseLayerNodes().getFirst();
1426 List<String> childrenIds = new ArrayList<>(root.getChildrenIds());
1427 childrenIds.add("lyr:map5:map5topo");
1428 childrenIds.add("lyr:map5:map5topo_simple");
1429 childrenIds.add("lvl:luchtfoto-labels");
1430 root.setChildrenIds(childrenIds);
1431 app.getSettings()
1432 .putLayerSettingsItem("lyr:map5:map5topo", new AppLayerSettings().title("Map5"))
1433 .putLayerSettingsItem("lyr:map5:map5topo_simple", new AppLayerSettings().title("Map5 simple"));
1434 app.getContentRoot()
1435 .addBaseLayerNodesItem(new AppTreeLayerNode()
1436 .objectType("AppTreeLayerNode")
1437 .id("lyr:map5:map5topo")
1438 .serviceId("map5")
1439 .layerName("map5topo")
1440 .visible(false))
1441 .addBaseLayerNodesItem(new AppTreeLayerNode()
1442 .objectType("AppTreeLayerNode")
1443 .id("lyr:map5:map5topo_simple")
1444 .serviceId("map5")
1445 .layerName("map5topo_simple")
1446 .visible(false))
1447 .addBaseLayerNodesItem(new AppTreeLevelNode()
1448 .objectType("AppTreeLevelNode")
1449 .id("lvl:luchtfoto-labels")
1450 .title("Luchtfoto met labels")
1451 .addChildrenIdsItem("lyr:map5:luforoadslabels")
1452 .addChildrenIdsItem("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2"))
1453 .addBaseLayerNodesItem(new AppTreeLayerNode()
1454 .objectType("AppTreeLayerNode")
1455 .id("lyr:map5:luforoadslabels")
1456 .serviceId("map5")
1457 .layerName("luforoadslabels")
1458 .visible(false))
1459 .addBaseLayerNodesItem(new AppTreeLayerNode()
1460 .objectType("AppTreeLayerNode")
1461 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR2")
1462 .serviceId("pdok-hwh-luchtfotorgb")
1463 .layerName("Actueel_orthoHR")
1464 .visible(false));
1465 }
1466
1467 applicationRepository.save(app);
1468
1469 app = new Application()
1470 .setName("base")
1471 .setTitle("Service base app")
1472 .setCrs("EPSG:28992")
1473 .setAuthorizationRules(ruleAnonymousRead)
1474 .setContentRoot(new AppContent()
1475 .addBaseLayerNodesItem(new AppTreeLevelNode()
1476 .objectType("AppTreeLevelNode")
1477 .id("root-base-layers")
1478 .root(true)
1479 .title("Base layers")
1480 .childrenIds(List.of(
1481 "lyr:openbasiskaart:osm", "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR"))));
1482 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1483 applicationRepository.save(app);
1484
1485 app = new Application()
1486 .setName("secured")
1487 .setTitle("secured app")
1488 .setCrs("EPSG:28992")
1489 .setAuthorizationRules(ruleLoggedIn)
1490 .setContentRoot(new AppContent()
1491 .addBaseLayerNodesItem(new AppTreeLevelNode()
1492 .objectType("AppTreeLevelNode")
1493 .id("root-base-layers")
1494 .root(true)
1495 .title("Base layers")
1496 .childrenIds(List.of(
1497 "lyr:openbasiskaart:osm",
1498 "lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR",
1499 "lyr:openbasiskaart-proxied:osm")))
1500 .addBaseLayerNodesItem(new AppTreeLayerNode()
1501 .objectType("AppTreeLayerNode")
1502 .id("lyr:openbasiskaart-proxied:osm")
1503 .serviceId("openbasiskaart-proxied")
1504 .layerName("osm")
1505 .visible(false))
1506 .addLayerNodesItem(new AppTreeLevelNode()
1507 .objectType("AppTreeLevelNode")
1508 .id("root")
1509 .root(true)
1510 .title("Layers")
1511 .childrenIds(List.of(
1512 "lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied",
1513 "lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied",
1514 "lvl:proxied")))
1515 .addLayerNodesItem(new AppTreeLayerNode()
1516 .objectType("AppTreeLayerNode")
1517 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Gemeentegebied")
1518 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1519 .layerName("Gemeentegebied")
1520 .visible(true))
1521 .addLayerNodesItem(new AppTreeLayerNode()
1522 .objectType("AppTreeLayerNode")
1523 .id("lyr:pdok-kadaster-bestuurlijkegebieden:Provinciegebied")
1524 .serviceId("pdok-kadaster-bestuurlijkegebieden")
1525 .layerName("Provinciegebied")
1526 .visible(false))
1527 .addLayerNodesItem(new AppTreeLevelNode()
1528 .objectType("AppTreeLevelNode")
1529 .id("lvl:proxied")
1530 .title("Proxied")
1531 .childrenIds(List.of("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")))
1532 .addLayerNodesItem(new AppTreeLayerNode()
1533 .objectType("AppTreeLayerNode")
1534 .id("lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel")
1535 .serviceId("snapshot-geoserver-proxied")
1536 .layerName("postgis:begroeidterreindeel")
1537 .visible(false)))
1538 .setSettings(
1539 new AppSettings()
1540 .putLayerSettingsItem(
1541 "lyr:openbasiskaart-proxied:osm",
1542 new AppLayerSettings().title("Openbasiskaart (proxied)"))
1543 .putLayerSettingsItem(
1544 "lyr:snapshot-geoserver-proxied:postgis:begroeidterreindeel",
1545 new AppLayerSettings()
1546 .description("This layer should render using purple polygons")
1547 .selectedStyles(
1548 List.of(
1549 new WMSStyle()
1550 .name("purple_polygon")
1551 .title("purple_polygon")
1552 .abstractText(null)
1553 .legendUrl(
1554 URI.create(
1555 "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"))))));
1556
1557 app.getContentRoot().getBaseLayerNodes().addAll(baseNodes);
1558 applicationRepository.save(app);
1559
1560 app = new Application()
1561 .setName("secured-auth")
1562 .setTitle("secured (with authorizations)")
1563 .setCrs("EPSG:28992")
1564 .setAuthorizationRules(List.of(
1565 new AuthorizationRule()
1566 .groupName("test-foo")
1567 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW)),
1568 new AuthorizationRule()
1569 .groupName("test-bar")
1570 .decisions(Map.of(ACCESS_TYPE_VIEW, AuthorizationRuleDecision.ALLOW))))
1571 .setContentRoot(new AppContent()
1572 .addLayerNodesItem(new AppTreeLevelNode()
1573 .objectType("AppTreeLevelNode")
1574 .id("root")
1575 .root(true)
1576 .title("Layers")
1577 .childrenIds(List.of("lvl:needs-auth", "lvl:public")))
1578 .addLayerNodesItem(new AppTreeLevelNode()
1579 .objectType("AppTreeLevelNode")
1580 .id("lvl:public")
1581 .title("Public")
1582 .childrenIds(List.of("lyr:snapshot-geoserver:BGT")))
1583 .addLayerNodesItem(new AppTreeLevelNode()
1584 .objectType("AppTreeLevelNode")
1585 .id("lvl:needs-auth")
1586 .title("Needs auth")
1587 .childrenIds(List.of(
1588 "lyr:filtered-snapshot-geoserver:BGT",
1589 "lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1590 .addLayerNodesItem(new AppTreeLayerNode()
1591 .objectType("AppTreeLayerNode")
1592 .id("lyr:filtered-snapshot-geoserver:BGT")
1593 .serviceId("filtered-snapshot-geoserver")
1594 .layerName("BGT")
1595 .visible(true))
1596 .addLayerNodesItem(new AppTreeLayerNode()
1597 .objectType("AppTreeLayerNode")
1598 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1599 .serviceId("filtered-snapshot-geoserver")
1600 .layerName("postgis:begroeidterreindeel")
1601 .visible(true))
1602 .addLayerNodesItem(new AppTreeLayerNode()
1603 .objectType("AppTreeLayerNode")
1604 .id("lyr:snapshot-geoserver:BGT")
1605 .serviceId("snapshot-geoserver")
1606 .layerName("BGT")
1607 .visible(true)));
1608
1609 applicationRepository.save(app);
1610
1611 app = new Application()
1612 .setName("austria")
1613 .setCrs("EPSG:3857")
1614 .setAuthorizationRules(ruleAnonymousRead)
1615 .setTitle("Austria")
1616 .setInitialExtent(
1617 new Bounds().minx(987982d).miny(5799551d).maxx(1963423d).maxy(6320708d))
1618 .setMaxExtent(
1619 new Bounds().minx(206516d).miny(5095461d).maxx(3146930d).maxy(7096232d))
1620 .setContentRoot(new AppContent()
1621 .addBaseLayerNodesItem(new AppTreeLevelNode()
1622 .objectType("AppTreeLevelNode")
1623 .id("root-base-layers")
1624 .root(true)
1625 .title("Base layers")
1626 .childrenIds(List.of(
1627 "lyr:at-basemap:geolandbasemap",
1628 "lyr:at-basemap:orthofoto",
1629 "lvl:orthofoto-labels",
1630 "lyr:osm:xyz")))
1631 .addBaseLayerNodesItem(new AppTreeLayerNode()
1632 .objectType("AppTreeLayerNode")
1633 .id("lyr:at-basemap:geolandbasemap")
1634 .serviceId("at-basemap")
1635 .layerName("geolandbasemap")
1636 .visible(true))
1637 .addBaseLayerNodesItem(new AppTreeLayerNode()
1638 .objectType("AppTreeLayerNode")
1639 .id("lyr:at-basemap:orthofoto")
1640 .serviceId("at-basemap")
1641 .layerName("bmaporthofoto30cm")
1642 .visible(false))
1643 .addBaseLayerNodesItem(new AppTreeLevelNode()
1644 .objectType("AppTreeLevelNode")
1645 .id("lvl:orthofoto-labels")
1646 .title("Orthophoto with labels")
1647 .childrenIds(List.of("lyr:at-basemap:bmapoverlay", "lyr:at-basemap:orthofoto_2")))
1648 .addBaseLayerNodesItem(new AppTreeLayerNode()
1649 .objectType("AppTreeLayerNode")
1650 .id("lyr:at-basemap:bmapoverlay")
1651 .serviceId("at-basemap")
1652 .layerName("bmapoverlay")
1653 .visible(false))
1654 .addBaseLayerNodesItem(new AppTreeLayerNode()
1655 .objectType("AppTreeLayerNode")
1656 .id("lyr:at-basemap:orthofoto_2")
1657 .serviceId("at-basemap")
1658 .layerName("bmaporthofoto30cm")
1659 .visible(false))
1660 .addBaseLayerNodesItem(new AppTreeLayerNode()
1661 .objectType("AppTreeLayerNode")
1662 .id("lyr:osm:xyz")
1663 .serviceId("osm")
1664 .layerName("xyz")
1665 .visible(false)));
1666
1667 applicationRepository.save(app);
1668
1669 app = new Application()
1670 .setName("3d_utrecht")
1671 .setCrs("EPSG:3857")
1672 .setAuthorizationRules(ruleLoggedIn)
1673 .setTitle("3D Utrecht")
1674 .setInitialExtent(
1675 new Bounds().minx(558390d).miny(6818485d).maxx(566751d).maxy(6824036d))
1676 .setMaxExtent(
1677 new Bounds().minx(91467d).miny(6496479d).maxx(1037043d).maxy(7147453d))
1678 .setSettings(new AppSettings().uiSettings(new AppUiSettings().enable3D(true)))
1679 .setContentRoot(new AppContent()
1680 .addBaseLayerNodesItem(new AppTreeLevelNode()
1681 .objectType("AppTreeLevelNode")
1682 .id("root-base-layers")
1683 .root(true)
1684 .title("Base layers")
1685 .childrenIds(List.of("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR", "lyr:osm:xyz")))
1686 .addBaseLayerNodesItem(new AppTreeLayerNode()
1687 .objectType("AppTreeLayerNode")
1688 .id("lyr:pdok-hwh-luchtfotorgb:Actueel_orthoHR")
1689 .serviceId("pdok-hwh-luchtfotorgb")
1690 .layerName("Actueel_orthoHR")
1691 .visible(true))
1692 .addBaseLayerNodesItem(new AppTreeLayerNode()
1693 .objectType("AppTreeLayerNode")
1694 .id("lyr:osm:xyz")
1695 .serviceId("osm")
1696 .layerName("xyz")
1697 .visible(false))
1698 .addLayerNodesItem(new AppTreeLevelNode()
1699 .objectType("AppTreeLevelNode")
1700 .id("root")
1701 .root(true)
1702 .title("Layers")
1703 .childrenIds(List.of(
1704 "lyr:3dbag_utrecht:tiles3d",
1705 "lyr:snapshot-geoserver:postgis:begroeidterreindeel",
1706 "lyr:3d_basisvoorziening_gebouwen_proxy:tiles3d",
1707 "lyr:3d_utrecht_proxied_auth:tiles3d")))
1708 .addLayerNodesItem(new AppTreeLayerNode()
1709 .objectType("AppTreeLayerNode")
1710 .id("lyr:3dbag_utrecht:tiles3d")
1711 .serviceId("3dbag_utrecht")
1712 .layerName("tiles3d")
1713 .visible(true))
1714 .addLayerNodesItem(new AppTreeLayerNode()
1715 .objectType("AppTreeLayerNode")
1716 .id("lyr:3d_basisvoorziening_gebouwen_proxy:tiles3d")
1717 .serviceId("3d_basisvoorziening_gebouwen_proxy")
1718 .layerName("tiles3d")
1719 .visible(false))
1720 .addLayerNodesItem(new AppTreeLayerNode()
1721 .objectType("AppTreeLayerNode")
1722 .id("lyr:3d_utrecht_proxied_auth:tiles3d")
1723 .serviceId("3d_utrecht_proxied_auth")
1724 .layerName("tiles3d")
1725 .visible(false))
1726 .addLayerNodesItem(new AppTreeLayerNode()
1727 .objectType("AppTreeLayerNode")
1728 .id("lyr:snapshot-geoserver:postgis:begroeidterreindeel")
1729 .serviceId("snapshot-geoserver")
1730 .layerName("postgis:begroeidterreindeel")
1731 .visible(true))
1732 .addTerrainLayerNodesItem(new AppTreeLevelNode()
1733 .objectType("AppTreeLevelNode")
1734 .id("root-terrain-layers")
1735 .root(true)
1736 .title("Terrain Layers")
1737 .childrenIds(List.of("lyr:ahn_terrain_model:quantizedmesh")))
1738 .addTerrainLayerNodesItem(new AppTreeLayerNode()
1739 .objectType("AppTreeLayerNode")
1740 .id("lyr:ahn_terrain_model:quantizedmesh")
1741 .serviceId("ahn_terrain_model")
1742 .layerName("quantizedmesh")
1743 .visible(false)));
1744
1745 applicationRepository.save(app);
1746
1747 app = new Application()
1748 .setName("public-with-auth")
1749 .setTitle("Public app with one restricted layer in a group")
1750 .setCrs("EPSG:28992")
1751 .setAuthorizationRules(ruleAnonymousRead)
1752 .setInitialExtent(
1753 new Bounds().minx(130011d).miny(458031d).maxx(132703d).maxy(459995d))
1754 .setMaxExtent(
1755 new Bounds().minx(-285401d).miny(22598d).maxx(595401d).maxy(903401d))
1756 .setStyling(new AppStyling().logo(logo.getId().toString()))
1757 .setContentRoot(new AppContent()
1758 .addBaseLayerNodesItem(new AppTreeLevelNode()
1759 .objectType("AppTreeLevelNode")
1760 .id("root")
1761 .root(true)
1762 .title("Basemaps")
1763 .childrenIds(List.of("lyr:openbasiskaart:osm")))
1764 .addBaseLayerNodesItem(new AppTreeLayerNode()
1765 .objectType("AppTreeLayerNode")
1766 .id("lyr:openbasiskaart:osm")
1767 .serviceId("openbasiskaart")
1768 .layerName("osm")
1769 .visible(true))
1770 .addLayerNodesItem(new AppTreeLevelNode()
1771 .objectType("AppTreeLevelNode")
1772 .id("root")
1773 .root(true)
1774 .title("Application layers")
1775 .childrenIds(List.of(
1776 "lyr:snapshot-geoserver:postgis:kadastraal_perceel", "xpfhl34VmghkU12nP9Jer")))
1777 .addLayerNodesItem(new AppTreeLayerNode()
1778 .objectType("AppTreeLayerNode")
1779 .id("lyr:snapshot-geoserver:postgis:kadastraal_perceel")
1780 .serviceId("snapshot-geoserver")
1781 .layerName("postgis:kadastraal_perceel")
1782 .visible(true))
1783 .addLayerNodesItem(new AppTreeLevelNode()
1784 .id("xpfhl34VmghkU12nP9Jer")
1785 .root(false)
1786 .title("restricted")
1787 .objectType("AppTreeLevelNode")
1788 .childrenIds(List.of("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")))
1789 .addLayerNodesItem(new AppTreeLayerNode()
1790 .objectType("AppTreeLayerNode")
1791 .id("lyr:filtered-snapshot-geoserver:postgis:begroeidterreindeel")
1792 .visible(true)
1793 .serviceId("filtered-snapshot-geoserver")
1794 .layerName("postgis:begroeidterreindeel")))
1795 .setSettings(new AppSettings());
1796
1797 applicationRepository.save(app);
1798
1799 Configuration config = new Configuration();
1800 config.setKey(Configuration.DEFAULT_APP);
1801 config.setValue("default");
1802 configurationRepository.save(config);
1803 config = new Configuration();
1804 config.setKey(Configuration.DEFAULT_BASE_APP);
1805 config.setValue("base");
1806 configurationRepository.save(config);
1807 }
1808
1809 private void createConfigurationTestData() throws JacksonException {
1810 Configuration config = new Configuration();
1811 config.setKey("test");
1812 config.setAvailableForViewer(true);
1813 config.setValue("test value");
1814 config.setJsonValue(new JsonMapper().readTree("{ \"someProperty\": 1, \"nestedObject\": { \"num\": 42 } }"));
1815 configurationRepository.save(config);
1816 }
1817
1818 @Transactional
1819 public void createSolrIndex() throws Exception {
1820 if (connectToSpatialDbs) {
1821
1822
1823 featureSourceRepository.flush();
1824
1825 logger.info("Creating Solr index");
1826 @SuppressWarnings("PMD.AvoidUsingHardCodedIP")
1827 final String solrUrl = "http://" + (connectToSpatialDbsAtLocalhost ? "127.0.0.1" : "solr") + ":8983/solr/";
1828 this.solrService.setSolrUrl(solrUrl);
1829 SolrHelper solrHelper = new SolrHelper(this.solrService.getSolrClientForIndexing())
1830 .withBatchSize(solrBatchSize)
1831 .withGeometryValidationRule(solrGeometryValidationRule);
1832 GeoService geoService =
1833 geoServiceRepository.findById("snapshot-geoserver").orElseThrow();
1834 Application defaultApp = applicationRepository.findByName("default");
1835
1836 TMFeatureType begroeidterreindeelFT = geoService.findFeatureTypeForLayer(
1837 geoService.findLayer("postgis:begroeidterreindeel"), featureSourceRepository);
1838
1839 TMFeatureType wegdeelFT = geoService.findFeatureTypeForLayer(
1840 geoService.findLayer("sqlserver:wegdeel"), featureSourceRepository);
1841
1842 TMFeatureType kadastraalPerceelFT = geoService.findFeatureTypeForLayer(
1843 geoService.findLayer("postgis:kadastraal_perceel"), featureSourceRepository);
1844
1845 try (solrHelper) {
1846 SearchIndex begroeidterreindeelIndex = null;
1847 if (begroeidterreindeelFT != null) {
1848 begroeidterreindeelIndex = new SearchIndex()
1849 .setName("Begroeidterreindeel")
1850 .setFeatureTypeId(begroeidterreindeelFT.getId())
1851 .setSearchFieldsUsed(List.of("class", "plus_fysiekvoorkomen", "bronhouder"))
1852 .setSearchDisplayFieldsUsed(List.of("class", "plus_fysiekvoorkomen"));
1853 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1854 begroeidterreindeelIndex = solrHelper.addFeatureTypeIndex(
1855 begroeidterreindeelIndex,
1856 begroeidterreindeelFT,
1857 featureSourceFactoryHelper,
1858 searchIndexRepository);
1859 begroeidterreindeelIndex = searchIndexRepository.save(begroeidterreindeelIndex);
1860 }
1861
1862 SearchIndex kadastraalPerceelIndex = null;
1863 if (kadastraalPerceelFT != null) {
1864 kadastraalPerceelIndex = new SearchIndex()
1865 .setName("kadastraal_perceel")
1866 .setFeatureTypeId(kadastraalPerceelFT.getId())
1867 .setSearchFieldsUsed(List.of("aanduiding"))
1868 .setSearchDisplayFieldsUsed(List.of("aanduiding"));
1869 kadastraalPerceelIndex = searchIndexRepository.save(kadastraalPerceelIndex);
1870 kadastraalPerceelIndex = solrHelper.addFeatureTypeIndex(
1871 kadastraalPerceelIndex,
1872 kadastraalPerceelFT,
1873 featureSourceFactoryHelper,
1874 searchIndexRepository);
1875 kadastraalPerceelIndex = searchIndexRepository.save(kadastraalPerceelIndex);
1876 }
1877
1878 SearchIndex wegdeelIndex = null;
1879 if (wegdeelFT != null) {
1880 wegdeelIndex = new SearchIndex()
1881 .setName("Wegdeel")
1882 .setFeatureTypeId(wegdeelFT.getId())
1883 .setSearchFieldsUsed(List.of(
1884 "function_", "plus_fysiekvoorkomenwegdeel", "surfacematerial", "bronhouder"))
1885 .setSearchDisplayFieldsUsed(List.of("function_", "plus_fysiekvoorkomenwegdeel"));
1886 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1887 wegdeelIndex = solrHelper.addFeatureTypeIndex(
1888 wegdeelIndex, wegdeelFT, featureSourceFactoryHelper, searchIndexRepository);
1889 wegdeelIndex = searchIndexRepository.save(wegdeelIndex);
1890 }
1891
1892 featureSourceRepository
1893 .getByTitle("PostGIS")
1894 .flatMap(fs -> fs.getFeatureTypes().stream()
1895 .filter(ft -> ft.getName().equals("bak"))
1896 .findFirst())
1897 .ifPresent(ft -> {
1898 SearchIndex bak = new SearchIndex()
1899 .setName("bak")
1900 .setFeatureTypeId(ft.getId())
1901 .setSearchFieldsUsed(List.of("gmlid", "identificatie", "plus_type"))
1902 .setSearchDisplayFieldsUsed(List.of("gmlid", "plus_type", "bronhouder"));
1903 searchIndexRepository.save(bak);
1904 try {
1905 bak = solrHelper.addFeatureTypeIndex(
1906 bak, ft, featureSourceFactoryHelper, searchIndexRepository);
1907 searchIndexRepository.save(bak);
1908 } catch (IOException | SolrServerException e) {
1909 throw new RuntimeException(e);
1910 }
1911 });
1912
1913
1914
1915 featureSourceRepository
1916 .getByTitle("PostGIS OSM")
1917 .flatMap(fs -> fs.getFeatureTypes().stream()
1918 .filter(ft -> ft.getName().equals("osm_roads"))
1919 .findFirst())
1920 .ifPresent(ft -> {
1921 SearchIndex osm_no_pk = new SearchIndex()
1922 .setName("osm_no_pk")
1923 .setFeatureTypeId(ft.getId())
1924 .setSearchFieldsUsed(List.of("landuse", "osm_id", "natural", "boundary"))
1925 .setSearchDisplayFieldsUsed(
1926 List.of("landuse", "osm_id", "natural", "amenity", "boundary"));
1927 searchIndexRepository.save(osm_no_pk);
1928 });
1929
1930 AppTreeLayerNode begroeidTerreindeelLayerNode = defaultApp
1931 .getAllAppTreeLayerNode()
1932 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:postgis:begroeidterreindeel"))
1933 .findFirst()
1934 .orElse(null);
1935
1936 if (begroeidTerreindeelLayerNode != null && begroeidterreindeelIndex != null) {
1937 defaultApp
1938 .getAppLayerSettings(begroeidTerreindeelLayerNode)
1939 .setSearchIndexId(begroeidterreindeelIndex.getId());
1940 }
1941
1942 AppTreeLayerNode kadastraalPerceelLayerNode = defaultApp
1943 .getAllAppTreeLayerNode()
1944 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:postgis:kadastraal_perceel"))
1945 .findFirst()
1946 .orElse(null);
1947
1948 if (kadastraalPerceelLayerNode != null && kadastraalPerceelIndex != null) {
1949 defaultApp
1950 .getAppLayerSettings(kadastraalPerceelLayerNode)
1951 .setSearchIndexId(kadastraalPerceelIndex.getId());
1952 }
1953
1954 AppTreeLayerNode wegdeel = defaultApp
1955 .getAllAppTreeLayerNode()
1956 .filter(node -> node.getId().equals("lyr:snapshot-geoserver:sqlserver:wegdeel"))
1957 .findFirst()
1958 .orElse(null);
1959
1960 if (wegdeel != null && wegdeelIndex != null) {
1961 defaultApp.getAppLayerSettings(wegdeel).setSearchIndexId(wegdeelIndex.getId());
1962 }
1963
1964 applicationRepository.save(defaultApp);
1965 }
1966 }
1967 }
1968
1969 private void createSearchIndexTasks() {
1970 logger.info("Creating search index tasks");
1971 List.of("Begroeidterreindeel", "kadastraal_perceel")
1972 .forEach(name -> searchIndexRepository.findByName(name).ifPresent(index -> {
1973 index.setSchedule(new TaskSchedule()
1974
1975 .cronExpression("0 0 0/1 1/1 * ? *")
1976
1977
1978 .description("Update Solr index \"" + name + "\" every hour"));
1979 try {
1980 final UUID uuid = taskManagerService.createTask(
1981 IndexTask.class,
1982 new TMJobDataMap(Map.of(
1983 Task.TYPE_KEY,
1984 TaskType.INDEX,
1985 Task.DESCRIPTION_KEY,
1986 index.getSchedule().getDescription(),
1987 IndexTask.INDEX_KEY,
1988 index.getId().toString(),
1989 Task.PRIORITY_KEY,
1990 10)),
1991 index.getSchedule().getCronExpression());
1992
1993 index.getSchedule().setUuid(uuid);
1994 searchIndexRepository.save(index);
1995
1996 logger.info("Created task to update Solr index with key: {}", uuid);
1997 } catch (SchedulerException e) {
1998 logger.error("Error creating scheduled solr index task", e);
1999 }
2000 }));
2001 }
2002
2003 private void createPages() throws IOException {
2004 Upload logo = new Upload()
2005 .setCategory(Upload.CATEGORY_PORTAL_IMAGE)
2006 .setFilename("gradient.svg")
2007 .setMimeType("image/svg+xml")
2008 .setContent(new ClassPathResource("test/gradient-logo.svg").getContentAsByteArray())
2009 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2010 uploadRepository.save(logo);
2011
2012 Page loggedIn = new Page();
2013 loggedIn.setAuthorizationRules(ruleLoggedIn);
2014 loggedIn.setName("loggedIn");
2015 loggedIn.setType("page");
2016 loggedIn.setContent("About Tailormap");
2017 loggedIn.setContent("""
2018 # About Tailormap
2019 This is a page for logged in users.
2020 """);
2021 pageRepository.save(loggedIn);
2022
2023 Page about = new Page();
2024 about.setAuthorizationRules(ruleAnonymousRead);
2025 about.setName("about");
2026 about.setType("page");
2027 about.setContent("About Tailormap");
2028 about.setContent("""
2029 # About Tailormap
2030
2031 This is a page about *Tailormap*. It doesn't say much yet.
2032 """);
2033 pageRepository.save(about);
2034
2035 Page page = new Page();
2036 page.setAuthorizationRules(ruleAnonymousRead);
2037 page.setName("home");
2038 page.setType("page");
2039 page.setTitle("Tailormap - Home");
2040 page.setContent("""
2041 # Welcome to Tailormap!
2042
2043 This page is only visible when you implement a frontend to display pages, or get it (including a simple CMS)
2044 from [B3Partners](https:
2045 """);
2046 page.setClassName(null);
2047 page.setTiles(List.of(
2048 new PageTile()
2049 .authorizationRules(ruleAnonymousRead)
2050 .id(UUID.randomUUID().toString())
2051 .title("Default app")
2052 .tileType(PageTile.TileTypeEnum.APPLICATION)
2053 .applicationId(Optional.ofNullable(applicationRepository.findByName("default"))
2054 .map(Application::getId)
2055 .orElse(null))
2056 .image(logo.getId().toString())
2057 .content("*Default app* tile content")
2058 .filterRequireAuthorization(false)
2059 .openInNewWindow(false),
2060 new PageTile()
2061 .authorizationRules(ruleAnonymousRead)
2062 .id(UUID.randomUUID().toString())
2063 .title("Secured app")
2064 .tileType(PageTile.TileTypeEnum.APPLICATION)
2065 .applicationId(Optional.ofNullable(applicationRepository.findByName("secured"))
2066 .map(Application::getId)
2067 .orElse(null))
2068 .filterRequireAuthorization(true)
2069 .content("Secure app, only shown if user has authorization")
2070 .openInNewWindow(false),
2071 new PageTile()
2072 .authorizationRules(ruleAnonymousRead)
2073 .id(UUID.randomUUID().toString())
2074 .title("Secured app (unfiltered)")
2075 .tileType(PageTile.TileTypeEnum.APPLICATION)
2076 .applicationId(Optional.ofNullable(applicationRepository.findByName("secured"))
2077 .map(Application::getId)
2078 .orElse(null))
2079 .filterRequireAuthorization(false)
2080 .content("Secure app, tile shown to everyone")
2081 .openInNewWindow(false),
2082 new PageTile()
2083 .authorizationRules(ruleAnonymousRead)
2084 .id(UUID.randomUUID().toString())
2085 .title("About")
2086 .tileType(PageTile.TileTypeEnum.PAGE)
2087 .pageId(about.getId())
2088 .openInNewWindow(false),
2089 new PageTile()
2090 .authorizationRules(ruleAnonymousRead)
2091 .id(UUID.randomUUID().toString())
2092 .title("B3Partners")
2093 .tileType(PageTile.TileTypeEnum.URL)
2094 .url("https://www.b3partners.nl/")
2095 .openInNewWindow(true),
2096 new PageTile()
2097 .authorizationRules(ruleLoggedIn)
2098 .id(UUID.randomUUID().toString())
2099 .title("Github repository")
2100 .tileType(PageTile.TileTypeEnum.URL)
2101 .url("https://github.com/Tailormap/tailormap-viewer")
2102 .openInNewWindow(true),
2103 new PageTile()
2104 .authorizationRules(ruleAnonymousRead)
2105 .id(UUID.randomUUID().toString())
2106 .tileType(PageTile.TileTypeEnum.PAGE)
2107 .title("Secured page")
2108 .pageId(loggedIn.getId())
2109 .filterRequireAuthorization(true)
2110 .openInNewWindow(true),
2111 new PageTile()
2112 .authorizationRules(ruleAnonymousRead)
2113 .id(UUID.randomUUID().toString())
2114 .tileType(PageTile.TileTypeEnum.PAGE)
2115 .title("Secured page (unfiltered)")
2116 .pageId(loggedIn.getId())
2117 .filterRequireAuthorization(false)
2118 .openInNewWindow(true)));
2119 pageRepository.save(page);
2120
2121 Configuration c = new Configuration();
2122 c.setKey(HOME_PAGE);
2123 c.setValue(page.getId().toString());
2124 configurationRepository.save(c);
2125
2126 List<MenuItem> globalMenuItems = List.of(
2127 new MenuItem().pageId(about.getId()).label("About").openInNewWindow(false),
2128 new MenuItem()
2129 .label("B3Partners website")
2130 .url("https://www.b3partners.nl/")
2131 .openInNewWindow(true)
2132 .exclusiveOnPageId(about.getId()));
2133 c = new Configuration();
2134 c.setKey(PORTAL_MENU);
2135 c.setJsonValue(new JsonMapper().valueToTree(globalMenuItems));
2136 configurationRepository.save(c);
2137 }
2138
2139 private void insertTestDrawing() {
2140
2141 try {
2142 this.jdbcClient.sql("""
2143 INSERT INTO data.drawing (id,name,description,domain_data,"access",created_by,created_at,updated_by,updated_at,srid,"version") VALUES
2144 ('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);
2145 """).update();
2146
2147 this.jdbcClient.sql("""
2148 INSERT INTO data.drawing_feature (drawing_id,id,geometry,properties) VALUES
2149 ('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),
2150 ('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),
2151 ('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)
2152 """).update();
2153 } catch (Exception any) {
2154 logger.error("Error inserting test drawing in data schema, some tests may fail", any);
2155 }
2156 }
2157
2158 private void insertDrawingStyle() throws IOException {
2159 Upload upload = new Upload()
2160 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2161 .setMimeType("image/svg+xml")
2162 .setFilename("ISO_7001_PI_PF_007.svg")
2163 .setContent(new ClassPathResource("test/ISO_7001_PI_PF_007.svg").getContentAsByteArray())
2164 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2165 upload = uploadRepository.save(upload);
2166 UUID drinkwaterImageId = upload.getId();
2167
2168 upload = new Upload()
2169 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2170 .setMimeType("image/svg+xml")
2171 .setFilename("lichtpunt.svg")
2172 .setContent(new ClassPathResource("test/lichtpunt.svg").getContentAsByteArray())
2173 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2174 upload = uploadRepository.save(upload);
2175 UUID lichtpuntImageId = upload.getId();
2176
2177 upload = new Upload()
2178 .setCategory(Upload.CATEGORY_DRAWING_STYLE_IMAGE)
2179 .setMimeType("image/svg+xml")
2180 .setFilename("ISO_7010_E003_-_First_aid_sign.svg")
2181 .setContent(new ClassPathResource("test/ISO_7010_E003_-_First_aid_sign.svg").getContentAsByteArray())
2182 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2183 upload = uploadRepository.save(upload);
2184 UUID firstAidImageId = upload.getId();
2185
2186 Properties props = new Properties();
2187 props.putAll(Map.of(
2188 "water-uuid", drinkwaterImageId.toString(),
2189 "lichtpunt-uuid", lichtpuntImageId.toString(),
2190 "first-aid-uuid", firstAidImageId.toString()));
2191
2192 String drawingStyles =
2193 new ClassPathResource("test/object-drawing-style.json").getContentAsString(StandardCharsets.UTF_8);
2194 drawingStyles = new PropertyPlaceholderHelper("${", "}").replacePlaceholders(drawingStyles, props);
2195
2196
2197 upload = new Upload()
2198 .setCategory(Upload.CATEGORY_DRAWING_STYLE)
2199 .setMimeType("application/json")
2200 .setFilename("object-drawing-style.json")
2201 .setContent(drawingStyles.getBytes(StandardCharsets.UTF_8))
2202 .setLastModified(OffsetDateTime.now(ZoneId.systemDefault()));
2203 uploadRepository.save(upload);
2204 }
2205 }