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