diff --git a/backend/examples/example-application.yaml b/backend/examples/example-application.yaml
index 2612864..30b8355 100644
--- a/backend/examples/example-application.yaml
+++ b/backend/examples/example-application.yaml
@@ -12,11 +12,20 @@ spring:
ldes:
streams:
- -
+ gipod:
member-type: "https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder"
timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
+ property-predicates:
+ startTime: "http://data.europa.eu/m8g/startTime"
+ endTime: "http://data.europa.eu/m8g/endTime"
+ verkeersmeting:
+ member-type: "https://data.vlaanderen.be/ns/verkeersmetingen#Verkeersmeting"
+ timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
+ property-predicates:
+ fullName: "http://custom/meetpunt#VolledigeNaam"
+ countObservationResult: "http://def.isotc211.org/iso19156/2011/CountObservation#OM_CountObservation.result"
graphdb:
url: "http://localhost:8080/rdf4j-server/repositories/"
repositoryId: "test"
server:
- port: 8080
\ No newline at end of file
+ port: 8084
\ No newline at end of file
diff --git a/backend/pom.xml b/backend/pom.xml
index d415c7e..023b3b9 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -66,6 +66,11 @@
hibernate-spatial
6.1.7.Final
+
+ io.hypersistence
+ hypersistence-utils-hibernate-60
+ 3.5.1
+
org.wololo
jts2geojson
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/EventStreamConfig.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/EventStreamConfig.java
index e234628..570824c 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/EventStreamConfig.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/EventStreamConfig.java
@@ -2,20 +2,14 @@
import org.springframework.context.annotation.Configuration;
+import java.util.HashMap;
+import java.util.Map;
+
@Configuration
public class EventStreamConfig {
- private String name;
private String memberType;
private String timestampPath;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
+ private Map propertyPredicates;
public String getMemberType() {
return memberType;
}
@@ -31,4 +25,15 @@ public String getTimestampPath() {
public void setTimestampPath(String timestampPath) {
this.timestampPath = timestampPath;
}
+
+ public Map getPropertyPredicates() {
+ if(propertyPredicates == null) {
+ propertyPredicates = new HashMap<>();
+ }
+ return propertyPredicates;
+ }
+
+ public void setPropertyPredicates(Map propertyPredicates) {
+ this.propertyPredicates = propertyPredicates;
+ }
}
\ No newline at end of file
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/StreamsConfig.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/StreamsConfig.java
index c6682c1..4aee41c 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/StreamsConfig.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/config/StreamsConfig.java
@@ -4,21 +4,27 @@
import org.springframework.context.annotation.Configuration;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
@Configuration
@ConfigurationProperties(prefix = "ldes")
public class StreamsConfig {
- private List streams;
+ private Map streams;
- public List getStreams() {
+ public Map getStreams() {
return streams;
}
+ public Optional getStream(String collection) {
+ return Optional.ofNullable(streams.get(collection));
+ }
+
public List getMemberTypes() {
- return streams.stream().map(EventStreamConfig::getMemberType).toList();
+ return streams.values().stream().map(EventStreamConfig::getMemberType).toList();
}
- public void setStreams(List streams) {
+ public void setStreams(Map streams) {
this.streams = streams;
}
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImpl.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImpl.java
index 7d7780e..e5d1f44 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImpl.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImpl.java
@@ -3,6 +3,7 @@
import be.informatievlaanderen.vsds.demonstrator.member.application.config.EventStreamConfig;
import be.informatievlaanderen.vsds.demonstrator.member.application.config.StreamsConfig;
import be.informatievlaanderen.vsds.demonstrator.member.application.exceptions.InvalidGeometryProvidedException;
+import be.informatievlaanderen.vsds.demonstrator.member.application.exceptions.MissingCollectionException;
import be.informatievlaanderen.vsds.demonstrator.member.application.exceptions.ResourceNotFoundException;
import be.informatievlaanderen.vsds.demonstrator.member.application.valueobjects.IngestedMemberDto;
import be.informatievlaanderen.vsds.demonstrator.member.application.valueobjects.MemberDto;
@@ -43,9 +44,12 @@ public MemberServiceImpl(MemberRepository repository, StreamsConfig streams, Mes
@Override
public void ingestMember(IngestedMemberDto ingestedMemberDto) {
try {
- Member member = ingestedMemberDto.getMember(streams.getStreams());
+ EventStreamConfig eventStreamConfig = streams.getStream(ingestedMemberDto.getCollection())
+ .orElseThrow(() -> new MissingCollectionException(ingestedMemberDto.getCollection()));
+ Member member = ingestedMemberDto.getMember(eventStreamConfig);
repository.saveMember(member);
- messageController.send(new MemberDto(member.getMemberId(), geoJSONWriter.write(member.getGeometry()), member.getTimestamp()), ingestedMemberDto.getCollection());
+ MemberDto memberDto = new MemberDto(member.getMemberId(), geoJSONWriter.write(member.getGeometry()), member.getTimestamp(), member.getProperties());
+ messageController.send(memberDto, ingestedMemberDto.getCollection());
log.info("new member ingested");
} catch (FactoryException | TransformException e) {
@@ -58,14 +62,14 @@ public List getMembersInRectangle(Geometry rectangleGeometry, String
var duration = Duration.parse(timePeriod).dividedBy(2);
return repository.getMembersByGeometry(rectangleGeometry, collectionName, timestamp.minus(duration), timestamp.plus(duration))
.stream()
- .map(memberGeometry -> new MemberDto(memberGeometry.getMemberId(), geoJSONWriter.write(memberGeometry.getGeometry()), memberGeometry.getTimestamp()))
+ .map(memberGeometry -> new MemberDto(memberGeometry.getMemberId(), geoJSONWriter.write(memberGeometry.getGeometry()), memberGeometry.getTimestamp(), memberGeometry.getProperties()))
.toList();
}
@Override
public MemberDto getMemberById(String memberId) {
return repository.findByMemberId(memberId)
- .map(memberGeometry -> new MemberDto(memberGeometry.getMemberId(), geoJSONWriter.write(memberGeometry.getGeometry()), memberGeometry.getTimestamp()))
+ .map(memberGeometry -> new MemberDto(memberGeometry.getMemberId(), geoJSONWriter.write(memberGeometry.getGeometry()), memberGeometry.getTimestamp(), memberGeometry.getProperties()))
.orElseThrow(() -> new ResourceNotFoundException("Member", memberId));
}
@@ -86,8 +90,8 @@ public LineChartDto getLineChartDtos() {
streams
.getStreams()
+ .keySet()
.stream()
- .map(EventStreamConfig::getName)
.map(collection -> {
long numberOfMembers = getNumberOfMembersByCollection(collection);
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImpl.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImpl.java
index ffb120c..a033844 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImpl.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImpl.java
@@ -7,9 +7,6 @@
import org.apache.jena.rdf.model.Model;
import org.springframework.stereotype.Service;
-import java.util.Objects;
-import java.util.Optional;
-
@Service
public class MemberValidatorImpl implements MemberValidator {
@@ -21,13 +18,11 @@ public MemberValidatorImpl(StreamsConfig streams) {
@Override
public void validate(Model member, String collectionName) {
- Optional streamConfig = streams.getStreams().stream().filter(streams -> Objects.equals(streams.getName(), collectionName)).findFirst();
- if(streamConfig.isEmpty()) {
- throw new MissingCollectionException(collectionName);
- } else if (!testMembertype(member, streamConfig.get().getMemberType())) {
- throw new MembertypeException("todo", streamConfig.get().getMemberType());
+ EventStreamConfig streamConfig = streams.getStream(collectionName)
+ .orElseThrow(() -> new MissingCollectionException(collectionName));
+ if (!testMembertype(member, streamConfig.getMemberType())) {
+ throw new MembertypeException("todo", streamConfig.getMemberType());
}
-
}
private boolean testMembertype(Model member, String memberType) {
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDto.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDto.java
index 9523f21..9309cf0 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDto.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDto.java
@@ -3,7 +3,6 @@
import be.informatievlaanderen.vsds.demonstrator.member.application.config.EventStreamConfig;
import be.informatievlaanderen.vsds.demonstrator.member.application.exceptions.NoGeometryProvidedException;
import be.informatievlaanderen.vsds.demonstrator.member.domain.member.entities.Member;
-import org.apache.jena.ext.com.google.common.collect.Iterables;
import org.apache.jena.geosparql.implementation.GeometryWrapper;
import org.apache.jena.geosparql.implementation.vocabulary.SRS_URI;
import org.apache.jena.rdf.model.*;
@@ -14,13 +13,16 @@
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
import static org.apache.jena.rdf.model.ResourceFactory.createResource;
public class IngestedMemberDto {
+ private static final Property RDF_SYNTAX_TYPE = createProperty("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
private final String collection;
private final Model model;
@@ -29,24 +31,29 @@ public IngestedMemberDto(String collection, Model model) {
this.model = model;
}
+ public String getCollection() {
+ return collection;
+ }
+
public Model getModel() {
return model;
}
- public Member getMember(List streams) throws FactoryException, TransformException {
- Geometry geometry = getMember();
- String memberId = getMemberId(streams);
- String timestampString = (String) getTimestampPath(streams);
+ public Member getMember(EventStreamConfig eventStreamConfig) throws FactoryException, TransformException {
+ Geometry geometry = getGeometry();
+ String memberId = getMemberId(eventStreamConfig);
+ String timestampString = getTimestampPath(eventStreamConfig, memberId);
+ Map properties = getProperties(eventStreamConfig);
LocalDateTime timestamp;
if (timestampString.contains("Z") || timestampString.contains("+")) {
timestamp = ZonedDateTime.parse(timestampString).toLocalDateTime();
} else {
timestamp = LocalDateTime.parse(timestampString);
}
- return new Member(memberId, collection, geometry, timestamp);
+ return new Member(memberId, collection, geometry, timestamp, properties);
}
- private Geometry getMember() throws FactoryException, TransformException {
+ private Geometry getGeometry() throws FactoryException, TransformException {
List wktNodes = model.listObjectsOfProperty(model.createProperty("http://www.opengis.net/ont/geosparql#asWKT")).toList();
if (wktNodes.isEmpty()) {
throw new NoGeometryProvidedException();
@@ -57,29 +64,29 @@ private Geometry getMember() throws FactoryException, TransformException {
}
}
- private Statement getMemberProperty(List streams, Function createSelector) {
- return Iterables.getOnlyElement(streams
- .stream()
- .filter(stream -> stream.getName().equals(collection))
- .map(stream -> model.listStatements(createSelector.apply(stream)))
- .map(StmtIterator::nextStatement)
- .toList());
+ private Statement getMemberProperty(EventStreamConfig config, Function createSelector) {
+ return model.listStatements(createSelector.apply(config)).nextStatement();
}
- private String getMemberId(List streams) {
- return getMemberProperty(streams, stream -> new SelectorImpl(null, null, createResource(stream.getMemberType())))
+ private String getMemberId(EventStreamConfig streamConfig) {
+ return getMemberProperty(streamConfig, stream -> new SelectorImpl(null, RDF_SYNTAX_TYPE, createResource(stream.getMemberType())))
.getSubject()
.getURI();
}
- private Object getTimestampPath(List streams) {
- return getMemberProperty(streams, stream -> new SelectorImpl(null, createProperty(stream.getTimestampPath()), (Resource) null))
+ private String getTimestampPath(EventStreamConfig streamConfig, String memberId) {
+ return getMemberProperty(streamConfig, stream -> new SelectorImpl(createResource(memberId), createProperty(stream.getTimestampPath()), (Resource) null))
.getObject()
.asLiteral().getString();
-
}
- public String getCollection() {
- return collection;
+ private Map getProperties(EventStreamConfig streamConfig) {
+ final Map properties = new HashMap<>();
+ for (Map.Entry propertyPredicateEntry : streamConfig.getPropertyPredicates().entrySet()) {
+ model.listStatements(null, createProperty(propertyPredicateEntry.getValue()), (Resource) null)
+ .nextOptional()
+ .ifPresent(statement -> properties.put(propertyPredicateEntry.getKey(), statement.getLiteral().getString()));
+ }
+ return properties;
}
}
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/MemberDto.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/MemberDto.java
index b683cda..c42a196 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/MemberDto.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/MemberDto.java
@@ -3,16 +3,19 @@
import org.wololo.geojson.Geometry;
import java.time.LocalDateTime;
+import java.util.Map;
public class MemberDto {
private final String memberId;
private final Geometry geojsonGeometry;
private final LocalDateTime timestamp;
+ private final Map properties;
- public MemberDto(String memberId, Geometry geojsonGeometry, LocalDateTime timestamp) {
+ public MemberDto(String memberId, Geometry geojsonGeometry, LocalDateTime timestamp, Map properties) {
this.memberId = memberId;
this.geojsonGeometry = geojsonGeometry;
this.timestamp = timestamp;
+ this.properties = properties;
}
public String getMemberId() {
@@ -26,4 +29,8 @@ public Geometry getGeojsonGeometry() {
public LocalDateTime getTimestamp() {
return timestamp;
}
+
+ public Map getProperties() {
+ return properties;
+ }
}
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/custom/MeetPuntRepository.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/custom/MeetPuntRepository.java
index a166d08..e39e8e0 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/custom/MeetPuntRepository.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/custom/MeetPuntRepository.java
@@ -24,7 +24,7 @@
@Component
public class MeetPuntRepository {
- private final Map meetpuntLocaties;
+ private final Map> meetpuntLocaties;
public MeetPuntRepository() {
this.meetpuntLocaties = new HashMap<>();
@@ -42,13 +42,18 @@ private void initializeMeetpunten() {
for (Element e : doc.select("meetpunt")) {
String meetpuntId = e.attr("unieke_id");
String wkt = "POINT (" + e.select("lengtegraad_EPSG_4326").get(0).text().replace(",", ".") + " " + e.select("breedtegraad_EPSG_4326").get(0).text().replace(",", ".") + ")";
+ String fullName = e.select("volledige_naam").get(0).text();
+ var statements = List.of(
+ createStatement(createResource("http://custom/meetpunt"), createProperty("http://www.opengis.net/ont/geosparql#asWKT"), createTypedLiteral(wkt, TypeMapper.getInstance().getTypeByName("http://www.opengis.net/ont/geosparql#wktLiteral"))),
+ createStatement(createResource("http://custom/meetpunt"), createProperty("http://custom/meetpunt#VolledigeNaam"), createStringLiteral(fullName))
+ );
- meetpuntLocaties.put(meetpuntId, createStatement(createResource("http://custom"), createProperty("http://www.opengis.net/ont/geosparql#asWKT"), createTypedLiteral(wkt, TypeMapper.getInstance().getTypeByName("http://www.opengis.net/ont/geosparql#wktLiteral"))));
+ meetpuntLocaties.put(meetpuntId, statements);
}
}
public List get(String s) {
- return List.of(meetpuntLocaties.get(s));
+ return meetpuntLocaties.get(s);
}
}
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/Member.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/Member.java
index ae02dca..0aa6edd 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/Member.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/Member.java
@@ -4,18 +4,21 @@
import org.locationtech.jts.geom.Geometry;
import java.time.LocalDateTime;
+import java.util.Map;
public class Member {
private final String memberId;
private final String collection;
private final Geometry geometry;
private final LocalDateTime timestamp;
+ private final Map properties;
- public Member(String memberId, String collection, Geometry geometry, LocalDateTime timestamp) {
+ public Member(String memberId, String collection, Geometry geometry, LocalDateTime timestamp, Map properties) {
this.memberId = memberId;
this.collection = collection;
this.geometry = geometry;
this.timestamp = timestamp;
+ this.properties = properties;
}
public String getMemberId() {
@@ -34,6 +37,10 @@ public String getCollection() {
return collection;
}
+ public Map getProperties() {
+ return properties;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberEntity.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberEntity.java
index e1a6f47..9983d9e 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberEntity.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberEntity.java
@@ -1,10 +1,15 @@
package be.informatievlaanderen.vsds.demonstrator.member.infra;
+import be.informatievlaanderen.vsds.demonstrator.member.domain.member.entities.Member;
+import io.hypersistence.utils.hibernate.type.basic.PostgreSQLHStoreType;
+import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
+import org.hibernate.annotations.Type;
import org.locationtech.jts.geom.Geometry;
import java.time.LocalDateTime;
+import java.util.Map;
@Entity(name = "member_entity")
public class MemberEntity {
@@ -13,15 +18,19 @@ public class MemberEntity {
private String collection;
private Geometry geometry;
private LocalDateTime timestamp;
+ @Type(value = PostgreSQLHStoreType.class)
+ @Column(columnDefinition = "hstore")
+ private Map properties;
public MemberEntity() {
}
- public MemberEntity(String memberId, String collection, Geometry geometry, LocalDateTime timestamp) {
+ public MemberEntity(String memberId, String collection, Geometry geometry, LocalDateTime timestamp, Map properties) {
this.memberId = memberId;
this.collection = collection;
this.geometry = geometry;
this.timestamp = timestamp;
+ this.properties = properties;
}
public String getMemberId() {
@@ -39,4 +48,22 @@ public LocalDateTime getTimestamp() {
public String getCollection() {
return collection;
}
+
+ public Map getProperties() {
+ return properties;
+ }
+
+ public Member toDomainObject() {
+ return new Member(memberId, collection, geometry, timestamp, properties);
+ }
+
+ public static MemberEntity fromDomainObject(Member member) {
+ return new MemberEntity(
+ member.getMemberId(),
+ member.getCollection(),
+ member.getGeometry(),
+ member.getTimestamp(),
+ member.getProperties()
+ );
+ }
}
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImpl.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImpl.java
index 0b429c2..24f7864 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImpl.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImpl.java
@@ -20,7 +20,7 @@ public MemberRepositoryImpl(MemberEntityJpaRepository memberGeometryJpaRepo) {
@Override
public void saveMember(Member member) {
- memberGeometryJpaRepo.save(new MemberEntity(member.getMemberId(), member.getCollection(), member.getGeometry(), member.getTimestamp()));
+ memberGeometryJpaRepo.save(MemberEntity.fromDomainObject(member));
}
@Override
@@ -28,7 +28,7 @@ public List getMembersByGeometry(Geometry geometry, String collectionNam
return memberGeometryJpaRepo
.getMemberGeometryEntitiesCoveredByGeometryInTimePeriodAndCollection(geometry, collectionName, startTime, endTime)
.stream()
- .map(mapEntityToMember())
+ .map(MemberEntity::toDomainObject)
.toList();
}
@@ -36,7 +36,7 @@ public List getMembersByGeometry(Geometry geometry, String collectionNam
public Optional findByMemberId(String memberId) {
return memberGeometryJpaRepo
.findById(memberId)
- .map(mapEntityToMember());
+ .map(MemberEntity::toDomainObject);
}
@Override
@@ -49,7 +49,7 @@ public List findMembersByCollectionAfterLocalDateTime(String collection,
return memberGeometryJpaRepo
.findByCollectionAndTimestampAfter(collection,localDateTime)
.stream()
- .map(mapEntityToMember())
+ .map(MemberEntity::toDomainObject)
.toList();
}
@@ -57,8 +57,4 @@ public List findMembersByCollectionAfterLocalDateTime(String collection,
public long getNumberOfMembersByCollection(String collection) {
return memberGeometryJpaRepo.countAllByCollection(collection);
}
-
- private Function mapEntityToMember() {
- return entity -> new Member(entity.getMemberId(), entity.getCollection(), entity.getGeometry(), entity.getTimestamp());
- }
}
diff --git a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/triple/infra/TripleRepositoryRDF4JImpl.java b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/triple/infra/TripleRepositoryRDF4JImpl.java
index da29a19..26619a1 100644
--- a/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/triple/infra/TripleRepositoryRDF4JImpl.java
+++ b/backend/src/main/java/be/informatievlaanderen/vsds/demonstrator/triple/infra/TripleRepositoryRDF4JImpl.java
@@ -41,12 +41,12 @@ protected void initRepo(RepositoryManager manager) {
RepositoryImplConfig repositoryImplConfig = new SailRepositoryConfig(storeConfig);
RepositoryConfig config = new RepositoryConfig(graphDBConfig.getRepositoryId(), repositoryImplConfig);
repositoryManager.addRepositoryConfig(config);
- log.info("Created repository with id: " + graphDBConfig.getRepositoryId());
+ log.info("Created repository with id: {}", graphDBConfig.getRepositoryId());
}
repository = repositoryManager.getRepository(graphDBConfig.getRepositoryId());
} catch (Exception e) {
- log.error("Could not create repository. Reason: " + e.getMessage());
+ log.error("Could not create repository. Reason: {}", e.getMessage());
}
}
diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml
index f8e0223..30b8355 100644
--- a/backend/src/main/resources/application.yaml
+++ b/backend/src/main/resources/application.yaml
@@ -1,25 +1,31 @@
-#spring:
-# datasource:
-# url: jdbc:postgresql://localhost:25432/test
-# username: postgres
-# password: test
-# jpa:
-# properties:
-# hibernate:
-# dialect: org.hibernate.dialect.PostgreSQLDialect
-# hibernate:
-# ddl-auto: update
-#
-#ldes:
-# streams:
-# - name: gipod
-# member-type: "https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder"
-# timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
-# - name: verkeersmeting
-# member-type: "https://data.vlaanderen.be/ns/verkeersmetingen#Verkeersmeting"
-# timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
-#graphdb:
-# url: "http://localhost:8080/rdf4j-server/repositories/"
-# repositoryId: "test"
-#server:
-# port: 8084
\ No newline at end of file
+spring:
+ datasource:
+ url: jdbc:postgresql://localhost:25432/test
+ username: postgres
+ password: test
+ jpa:
+ properties:
+ hibernate:
+ dialect: org.hibernate.dialect.PostgreSQLDialect
+ hibernate:
+ ddl-auto: update
+
+ldes:
+ streams:
+ gipod:
+ member-type: "https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder"
+ timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
+ property-predicates:
+ startTime: "http://data.europa.eu/m8g/startTime"
+ endTime: "http://data.europa.eu/m8g/endTime"
+ verkeersmeting:
+ member-type: "https://data.vlaanderen.be/ns/verkeersmetingen#Verkeersmeting"
+ timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
+ property-predicates:
+ fullName: "http://custom/meetpunt#VolledigeNaam"
+ countObservationResult: "http://def.isotc211.org/iso19156/2011/CountObservation#OM_CountObservation.result"
+graphdb:
+ url: "http://localhost:8080/rdf4j-server/repositories/"
+ repositoryId: "test"
+server:
+ port: 8084
\ No newline at end of file
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImplTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImplTest.java
index 951232c..85cf85e 100644
--- a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImplTest.java
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberServiceImplTest.java
@@ -29,6 +29,7 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
@@ -58,11 +59,10 @@ static void beforeAll() throws ParseException {
@BeforeEach
void setUp() {
EventStreamConfig eventStreamConfig = new EventStreamConfig();
- eventStreamConfig.setName(COLLECTION);
eventStreamConfig.setMemberType("https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder");
eventStreamConfig.setTimestampPath("http://www.w3.org/ns/prov#generatedAtTime");
StreamsConfig streams = new StreamsConfig();
- streams.setStreams(List.of(eventStreamConfig));
+ streams.setStreams(Map.of(COLLECTION, eventStreamConfig));
service = new MemberServiceImpl(repository, streams, mock(MessageController.class));
}
@@ -75,7 +75,7 @@ void when_GetMembersInRectangle_then_ReturnListOfMemberDtos() throws ParseExcept
when(repository.getMembersByGeometry(rectangle, COLLECTION, startTime, endTime)).thenReturn(members);
final List retrievedMembers = service.getMembersInRectangle(rectangle, COLLECTION, timestamp, TIME_PERIOD).stream()
- .map(dto -> new Member(dto.getMemberId(), COLLECTION, geoJSONReader.read(dto.getGeojsonGeometry()), dto.getTimestamp()))
+ .map(dto -> new Member(dto.getMemberId(), COLLECTION, geoJSONReader.read(dto.getGeojsonGeometry()), dto.getTimestamp(), Map.of()))
.toList();
assertEquals(members, retrievedMembers);
verify(repository).getMembersByGeometry(rectangle, COLLECTION, startTime, endTime);
@@ -104,7 +104,7 @@ class GetMemberById {
@Test
void when_MemberIsPresent_then_ReturnMember() {
- Member memberGeometry = new Member(ID, COLLECTION, rectangle, timestamp);
+ Member memberGeometry = new Member(ID, COLLECTION, rectangle, timestamp, Map.of());
when(repository.findByMemberId(ID)).thenReturn(Optional.of(memberGeometry));
@@ -165,14 +165,14 @@ void test_getLineChartDto() {
}
private List getMemberList() {
- Member id1 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id2 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id3 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id4 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id5 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id6 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id7 = new Member("id1", COLLECTION, null, LocalDateTime.now());
- Member id8 = new Member("id1", COLLECTION, null, LocalDateTime.now());
+ Member id1 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id2 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id3 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id4 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id5 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id6 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id7 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
+ Member id8 = new Member("id1", COLLECTION, null, LocalDateTime.now(), Map.of());
return List.of(id1, id2, id3, id4, id5, id6, id7, id8);
}
@@ -182,7 +182,7 @@ private List initMembers() throws ParseException {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
Geometry geometry = reader.read("POINT(%d %d)".formatted(i, j));
- members.add(new Member("id-%d".formatted(i * 6 + j), COLLECTION, geometry, timestamp));
+ members.add(new Member("id-%d".formatted(i * 6 + j), COLLECTION, geometry, timestamp, Map.of()));
}
}
return members;
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImplTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImplTest.java
index a84140c..8de4a29 100644
--- a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImplTest.java
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/services/MemberValidatorImplTest.java
@@ -7,24 +7,22 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import java.util.List;
+import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
class MemberValidatorImplTest {
+ private static final String COLLECTION = "collection";
- private StreamsConfig configs;
- private EventStreamConfig streamConfig;
private MemberValidatorImpl memberValidator;
@BeforeEach
void setUp() {
- configs = new StreamsConfig();
- streamConfig = new EventStreamConfig();
- streamConfig.setName("collection");
- configs.setStreams(List.of(streamConfig));
+ StreamsConfig configs = new StreamsConfig();
+ EventStreamConfig streamConfig = new EventStreamConfig();
+ configs.setStreams(Map.of(COLLECTION, streamConfig));
memberValidator = new MemberValidatorImpl(configs);
}
@@ -32,7 +30,7 @@ void setUp() {
void testCollectionNamePresent() {
Model member = mock(Model.class);
- assertDoesNotThrow(() -> memberValidator.validate(member, "collection"));
+ assertDoesNotThrow(() -> memberValidator.validate(member, COLLECTION));
assertThrows(MissingCollectionException.class, () -> memberValidator.validate(member, "notPresent"));
}
}
\ No newline at end of file
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDtoTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDtoTest.java
new file mode 100644
index 0000000..9b6dc4f
--- /dev/null
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/application/valueobjects/IngestedMemberDtoTest.java
@@ -0,0 +1,49 @@
+package be.informatievlaanderen.vsds.demonstrator.member.application.valueobjects;
+
+import be.informatievlaanderen.vsds.demonstrator.member.application.config.EventStreamConfig;
+import be.informatievlaanderen.vsds.demonstrator.member.domain.member.entities.Member;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class IngestedMemberDtoTest {
+ private static final String ID = "https://data.vlaanderen.be/id/verkeersmetingen/Verkeersmeting/3774/aantal/5/2023-10-16T09:01:43.861Z";
+ private static final String COLLECTION = "verkeersmeting";
+ private static EventStreamConfig eventStreamConfig;
+
+ @BeforeAll
+ static void beforeAll() {
+ eventStreamConfig = new EventStreamConfig();
+ eventStreamConfig.setMemberType("https://data.vlaanderen.be/ns/verkeersmetingen#Verkeersmeting");
+ eventStreamConfig.setTimestampPath("http://www.w3.org/ns/prov#generatedAtTime");
+ eventStreamConfig.setPropertyPredicates(Map.of(
+ "fullName", "http://custom/meetpunt#VolledigeNaam",
+ "countObservationResult", "http://def.isotc211.org/iso19156/2011/CountObservation#OM_CountObservation.result",
+ "observationResult", "http://def.isotc211.org/iso19156/2011/Observation#OM_Observation.result"
+ ));
+ }
+
+ @Test
+ void test_ingestion() throws FactoryException, TransformException {
+ Model model = RDFParser.source("members/traffic-count.nq").lang(Lang.NQUADS).toModel();
+ IngestedMemberDto ingestedMember = new IngestedMemberDto(COLLECTION, model);
+ Map properties = Map.of(
+ "fullName", "Kennedy snede 3",
+ "countObservationResult", "4",
+ "observationResult", "4"
+ );
+
+ Member member = ingestedMember.getMember(eventStreamConfig);
+
+ assertEquals(ID, member.getMemberId());
+ assertEquals(properties, member.getProperties());
+ }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/MemberTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/MemberTest.java
index 1a9f106..e7fc3d4 100644
--- a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/MemberTest.java
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/entities/MemberTest.java
@@ -13,6 +13,7 @@
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
+import java.util.Map;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -32,7 +33,7 @@ static void beforeAll() throws ParseException {
final WKTReader reader = new WKTReader();
geometry = reader.read("POINT(5 5)");
otherGeometry = reader.read("POINT(10 20)");
- member = new Member(ID, COLLECTION, geometry, timestamp);
+ member = new Member(ID, COLLECTION, geometry, timestamp, Map.of());
}
@ParameterizedTest(name = "{0}")
@@ -46,8 +47,8 @@ void test_inequality(String name, Object other) {
@Test
void test_equality() {
- Member other = new Member(ID, COLLECTION, geometry, timestamp);
- Member other2 = new Member(ID, COLLECTION, otherGeometry, timestamp);
+ Member other = new Member(ID, COLLECTION, geometry, timestamp, Map.of());
+ Member other2 = new Member(ID, COLLECTION, otherGeometry, timestamp, Map.of());
assertEquals(member, member);
assertEquals(member, other);
@@ -61,10 +62,10 @@ static class MemberGeometryArgumentsProvider implements ArgumentsProvider {
public Stream provideArguments(ExtensionContext extensionContext) {
return Stream.of(
Arguments.of("null", null),
- Arguments.of("Other id, same geometry, same timestamp", new Member("other", COLLECTION, geometry, timestamp)),
- Arguments.of("Other id, other geometry, same timestamp", new Member("not-the-same", COLLECTION, otherGeometry, timestamp)),
- Arguments.of("Other id, same geometry, other timestamp", new Member("no-equal-id", COLLECTION, geometry, otherTimestamp)),
- Arguments.of("Other id, other geometry, other timestamp", new Member("random-id-that-differs", COLLECTION, otherGeometry, otherTimestamp)),
+ Arguments.of("Other id, same geometry, same timestamp", new Member("other", COLLECTION, geometry, timestamp, Map.of())),
+ Arguments.of("Other id, other geometry, same timestamp", new Member("not-the-same", COLLECTION, otherGeometry, timestamp, Map.of())),
+ Arguments.of("Other id, same geometry, other timestamp", new Member("no-equal-id", COLLECTION, geometry, otherTimestamp, Map.of())),
+ Arguments.of("Other id, other geometry, other timestamp", new Member("random-id-that-differs", COLLECTION, otherGeometry, otherTimestamp, Map.of())),
Arguments.of("Not a member geometry", "Not a member geometry")
);
}
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/valueobjects/HourCountTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/valueobjects/HourCountTest.java
index 7c555b8..760941e 100644
--- a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/valueobjects/HourCountTest.java
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/domain/member/valueobjects/HourCountTest.java
@@ -15,14 +15,14 @@ class HourCountTest {
@Test
void test_HourCount(){
- Member id1 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 1, 5));
- Member id2 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 1, 5));
- Member id3 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 1, 15));
- Member id4 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 2, 5));
- Member id5 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 2, 25));
- Member id6 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 6, 1, 5));
- Member id7 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 6, 1, 2));
- Member id8 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 6, 2, 5));
+ Member id1 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 1, 5), Map.of());
+ Member id2 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 1, 5), Map.of());
+ Member id3 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 1, 15), Map.of());
+ Member id4 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 2, 5), Map.of());
+ Member id5 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 5, 2, 25), Map.of());
+ Member id6 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 6, 1, 5), Map.of());
+ Member id7 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 6, 1, 2), Map.of());
+ Member id8 = new Member("id1", COLLECTION, null, LocalDateTime.of(2023, 1, 6, 2, 5), Map.of());
HourCount hourCount = new HourCount(List.of(id1, id2, id3, id4, id5, id6, id7, id8));
Map memberCountByHour = hourCount.getMemberCountByHour();
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImplTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImplTest.java
index f0fc6cf..0f1cf0e 100644
--- a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImplTest.java
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/infra/MemberRepositoryImplTest.java
@@ -17,6 +17,7 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -53,8 +54,8 @@ void setUp() {
class RetrievalById {
@Test
void when_DbContainsMember_then_ReturnMemberInOptional() {
- final MemberEntity entity = new MemberEntity(ID, COLLECTION, point, timestamp);
- final Member member = new Member(ID, COLLECTION, point, timestamp);
+ final MemberEntity entity = new MemberEntity(ID, COLLECTION, point, timestamp, Map.of());
+ final Member member = new Member(ID, COLLECTION, point, timestamp, Map.of());
when(jpaRepository.findById(ID)).thenReturn(Optional.of(entity));
@@ -83,7 +84,7 @@ class RetrievalInRectangle {
void when_DbDoesContainMembers_then_ReturnMembersInRectangle() throws ParseException {
final List entities = initMembers();
final List members = entities.stream()
- .map(entity -> new Member(entity.getMemberId(), entity.getCollection(), entity.getGeometry(), timestamp))
+ .map(entity -> new Member(entity.getMemberId(), entity.getCollection(), entity.getGeometry(), timestamp, Map.of()))
.toList();
when(jpaRepository.getMemberGeometryEntitiesCoveredByGeometryInTimePeriodAndCollection(rectangle, COLLECTION, startTime, endTime)).thenReturn(entities);
@@ -105,7 +106,7 @@ void when_DbContainsOnlyMembersOutsideRectangle_then_ReturnEmptyList() {
@Test
void test_Saving() {
- Member member = new Member(ID, COLLECTION, point, timestamp);
+ Member member = new Member(ID, COLLECTION, point, timestamp, Map.of());
repository.saveMember(member);
@@ -122,10 +123,10 @@ void test_NumberCount() {
@Test
void test_findMembersAfterLocalDateTime() {
LocalDateTime now = LocalDateTime.now();
- List memberEntities = List.of(new MemberEntity(ID, COLLECTION, point, timestamp));
+ List memberEntities = List.of(new MemberEntity(ID, COLLECTION, point, timestamp, Map.of()));
when(jpaRepository.findByCollectionAndTimestampAfter(COLLECTION, now)).thenReturn(memberEntities);
List expectedMembers = memberEntities.stream()
- .map(entity -> new Member(entity.getMemberId(), entity.getCollection(), entity.getGeometry(), entity.getTimestamp()))
+ .map(entity -> new Member(entity.getMemberId(), entity.getCollection(), entity.getGeometry(), entity.getTimestamp(), Map.of()))
.toList();
List membersAfterLocalDateTime = repository.findMembersByCollectionAfterLocalDateTime(COLLECTION, now);
@@ -141,7 +142,7 @@ private List initMembers() throws ParseException {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
Geometry geometry = reader.read("POINT(%d %d)".formatted(i, j));
- members.add(new MemberEntity("id-%d".formatted(i * 6 + j), COLLECTION, geometry, timestamp));
+ members.add(new MemberEntity("id-%d".formatted(i * 6 + j), COLLECTION, geometry, timestamp, Map.of()));
}
}
return members;
diff --git a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/rest/MembersControllerTest.java b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/rest/MembersControllerTest.java
index cf4cc06..be46f0d 100644
--- a/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/rest/MembersControllerTest.java
+++ b/backend/src/test/java/be/informatievlaanderen/vsds/demonstrator/member/rest/MembersControllerTest.java
@@ -34,9 +34,11 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -74,7 +76,7 @@ class GetSingleMember {
@Test
void when_MemberGeometryPresent_then_MemberGeomtryIsReturned_and_StatusIs200() throws Exception {
final String json = transformToJson(ID, geoJSON, timestamp);
- MemberDto dto = new MemberDto(ID, geoJSON, timestamp);
+ MemberDto dto = new MemberDto(ID, geoJSON, timestamp, Map.of());
when(service.getMemberById(ID)).thenReturn(dto);
@@ -152,7 +154,7 @@ private List initMembers() throws ParseException {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
Geometry geometry = reader.read("POINT(%d %d)".formatted(i, j));
- members.add(new MemberDto("id-%d".formatted(i * 6 + j), geoJSONWriter.write(geometry), timestamp));
+ members.add(new MemberDto("id-%d".formatted(i * 6 + j), geoJSONWriter.write(geometry), timestamp, Map.of()));
}
}
return members;
diff --git a/backend/src/test/resources/application-test.yaml b/backend/src/test/resources/application-test.yaml
index 7d031f0..7060494 100644
--- a/backend/src/test/resources/application-test.yaml
+++ b/backend/src/test/resources/application-test.yaml
@@ -1,5 +1,5 @@
ldes:
streams:
- - name: gipod
+ gipod:
member-type: "https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder"
timestamp-path: "http://www.w3.org/ns/prov#generatedAtTime"
\ No newline at end of file
diff --git a/backend/src/test/resources/members/traffic-count.nq b/backend/src/test/resources/members/traffic-count.nq
new file mode 100644
index 0000000..6eac96d
--- /dev/null
+++ b/backend/src/test/resources/members/traffic-count.nq
@@ -0,0 +1,26 @@
+ .
+ "2023-10-16T10:00:00+01:00"^^ .
+ .
+ .
+ .
+ "Kennedy snede 3" .
+ "POINT (4.369845183 51.20728435)"^^ .
+ .
+ "2023-10-16T11:01:29+02:00"^^ .
+ "5"^^ .
+ "aantal"^^ .
+ .
+ "2023-10-16T10:00:00+01:00"^^ .
+ "4"^^ .
+ .
+ .
+ .
+ .
+ .
+ .
+ .
+ .
+ "2023-10-16T09:01:43.861Z"^^ .
+ "4"^^ .
+ .
+ .
diff --git a/docker-compose.yml b/docker-compose.yml
index acf5a36..c20883e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -12,6 +12,8 @@ services:
depends_on:
rdf4j-server:
condition: service_healthy
+ demonstrator:
+ condition: service_started
networks:
- demonstrator
@@ -27,12 +29,10 @@ services:
- SPRING_DATASOURCE_PASSWORD=test
- SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT=org.hibernate.dialect.PostgreSQLDialect
- SPRING_JPA_HIBERNATE_DDLAUTO=update
- - LDES_STREAMS_0_NAME=gipod
- - LDES_STREAMS_0_MEMBERTYPE=https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder
- - LDES_STREAMS_0_TIMESTAMPPATH=http://www.w3.org/ns/prov#generatedAtTime
- - LDES_STREAMS_1_NAME=verkeersmeting
- - LDES_STREAMS_1_MEMBERTYPE=https://data.vlaanderen.be/ns/verkeersmetingen#Verkeersmeting
- - LDES_STREAMS_1_TIMESTAMPPATH=http://www.w3.org/ns/prov#generatedAtTime
+ - LDES_STREAMS_GIPOD_MEMBERTYPE=https://data.vlaanderen.be/ns/mobiliteit#Mobiliteitshinder
+ - LDES_STREAMS_GIPOD_TIMESTAMPPATH=http://www.w3.org/ns/prov#generatedAtTime
+ - LDES_STREAMS_VERKEERSMETING_MEMBERTYPE=https://data.vlaanderen.be/ns/verkeersmetingen#Verkeersmeting
+ - LDES_STREAMS_VERKEERSMETING_TIMESTAMPPATH=http://www.w3.org/ns/prov#generatedAtTime
- GRAPHDB_URL=http://rdf4j-server:8080/rdf4j-server/repositories/
- GRAPHDB_REPOSITORYID=test
- SERVER_PORT=8080
@@ -53,8 +53,7 @@ services:
- 8080:8080
restart: on-failure
healthcheck:
- test: "curl --fail http://localhost:8080/rdf4j-workbench/repositories/test/summary || exit 1"
- interval: 15s
+ test: "curl --fail http://localhost:8080/rdf4j-workbench/repositories/NONE/repositories || exit 1"
networks:
- demonstrator
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 0a5c573..9ca5d88 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -3,8 +3,6 @@ import LeafletMap from './components/map/LeafletMap.vue'
import GlobalHeader from "@/components/headers/GlobalHeader.vue";
import MemberCounter from './components/membercounter/MemberCounter.vue'
import LineChart from "@/components/linechart/LineChart.vue";
-
-console.log(import.meta.env)
diff --git a/frontend/src/components/graph/KnowledgeGraph.vue b/frontend/src/components/graph/KnowledgeGraph.vue
index af02347..60159a6 100644
--- a/frontend/src/components/graph/KnowledgeGraph.vue
+++ b/frontend/src/components/graph/KnowledgeGraph.vue
@@ -47,7 +47,6 @@ export default {
font-family: Flanders Art Sans Regular, sans-serif;
font-style: normal;
font-weight: 400;
- font-size: 11px;
}
.link-text {
@@ -55,7 +54,6 @@ export default {
font-family: Flanders Art Sans Regular, sans-serif;
font-style: normal;
font-weight: 400;
- font-size: 9px;
}
diff --git a/frontend/src/components/graph/composables/useTriplesFetching.js b/frontend/src/components/graph/composables/useTriplesFetching.js
index db3d42d..f25d7cd 100644
--- a/frontend/src/components/graph/composables/useTriplesFetching.js
+++ b/frontend/src/components/graph/composables/useTriplesFetching.js
@@ -2,15 +2,19 @@ import axios from "axios";
import * as d3 from "d3";
import {triplesToGraph} from "@/components/graph/functions/triplesToGraph";
+const NODE_RADIUS = 8;
+const NODE_TEXT_FONT_SIZE = 11;
+const LINK_TEXT_FONT_SIZE = 9;
+
function visualizeTriples(triples) {
- let svg = d3.select("#knowledge-graph");
- let children = svg.selectAll("*")
- children.remove();
+ const svg = d3.select("#knowledge-graph");
+ svg.selectAll("*").remove();
+ const g = svg.append("g");
const width = +svg.style("width").replace("px", "")
const height = +svg.style("height").replace("px", "");
- let graph = triplesToGraph(triples);
- let force = d3.forceSimulation(graph.nodes);
+ const graph = triplesToGraph(triples);
+ const force = d3.forceSimulation(graph.nodes);
function dragstart() {
d3.select(this).classed("fixed", true);
@@ -28,8 +32,7 @@ function visualizeTriples(triples) {
const drag = d3.drag().on("start", dragstart).on("drag", dragged);
- svg
- .append("svg:defs")
+ g
.selectAll("marker")
.data(["end"])
.enter()
@@ -44,7 +47,7 @@ function visualizeTriples(triples) {
.append("svg:polyline")
.attr("points", "0,-5 10,0 0,5");
- let links = svg
+ const links = g
.selectAll(".link")
.data(graph.links)
.enter()
@@ -53,35 +56,46 @@ function visualizeTriples(triples) {
.attr("class", "link")
.attr("stroke-width", 1); //links
// ==================== Add Link Names =====================
- let linkTexts = svg
+ const linkTexts = g
.selectAll(".link-text")
.data(graph.links)
.enter()
.append("text")
.attr("class", "link-text")
+ .style("font-size", `${LINK_TEXT_FONT_SIZE}px`)
.text(function (d) {
return d.predicate;
});
// ==================== Add Link Names =====================
- let nodeTexts = svg
+ const nodeTexts = g
.selectAll(".node-text")
.data(graph.nodes)
.enter()
.append("text")
.attr("class", "node-text")
+ .style("font-size", `${NODE_TEXT_FONT_SIZE}px`)
.text(function (d) {
return d.label;
});
// ==================== Add Node =====================
- let nodes = svg
+ const nodes = g
.selectAll(".node")
.data(graph.nodes)
.enter()
.append("circle")
.attr("class", "node")
- .attr("r", 8)
+ .attr("r", NODE_RADIUS)
.call(drag);
+ let transform;
+
+ const zoom = d3.zoom().on("zoom", e => {
+ g.attr("transform", () => transform = e.transform);
+ nodes.attr("r", NODE_RADIUS / Math.sqrt(transform.k))
+ nodeTexts.style("font-size", `${11 / Math.sqrt(transform.k)}px`)
+ linkTexts.style("font-size", `${9 / Math.sqrt(transform.k)}px`)
+ })
+
function ticked() {
nodes
.attr("cx", function (d) {
@@ -123,6 +137,9 @@ function visualizeTriples(triples) {
}
force.on("tick", ticked);
+ svg
+ .call(zoom)
+ .call(zoom.transform, d3.zoomIdentity)
return force
.force(
diff --git a/frontend/src/components/map/LeafletMap.vue b/frontend/src/components/map/LeafletMap.vue
index e67552d..ff12bec 100644
--- a/frontend/src/components/map/LeafletMap.vue
+++ b/frontend/src/components/map/LeafletMap.vue
@@ -36,8 +36,8 @@ import MapButtons from "@/components/modal/MapButtons.vue";
const iconCreateFunction = (cluster) => {
const count = cluster.getChildCount();
- let clusterSize = "";
- let iconAnchor = [];
+ let clusterSize;
+ let iconAnchor;
let iconUrl;
if (count < 21) {
@@ -107,9 +107,9 @@ export default {
}).addTo(this.map);
//TODO: delete this and hard code the bounds of Flanders/Belgium for performance reasons
this.map.on("popupclose", () => this.memberId = null)
- this.map.on("moveend", () => {
- this.fetchMembers();
- });
+ // this.map.on("moveend", () => {
+ // this.fetchMembers();
+ // });
this.fetchMembers();
for (let [key, value] of this.layersToShow.entries()) {
if (value) {
@@ -129,10 +129,10 @@ export default {
this.memberId = null;
},
fetchMembers() {
- for (let [key, layer] of this.layers.entries()) {
+ for (let [collection, layer] of this.layers.entries()) {
axios({
method: 'post',
- url: `/api/${key}/in-rectangle`,
+ url: `/api/${collection}/in-rectangle`,
params: {
timestamp: new Date(this.time).toISOString().replace("Z", ""),
timePeriod: this.timePeriod
@@ -143,23 +143,22 @@ export default {
'Access-Control-Allow-Origin': '*'
}
}).then((response) => {
- this.handleMemberGeometries(layer, response.data)
+ this.handleMemberGeometries(collection, layer, response.data)
});
}
},
- handleMemberGeometries(layer, memberGeometries) {
+ handleMemberGeometries(collection, layer, memberGeometries) {
layer.clearLayers();
- let markers = useMarkers(memberGeometries, (memberId) => this.memberId = memberId, this.onPopupClosed)
+ let markers = useMarkers(memberGeometries, collection, (memberId) => this.memberId = memberId, this.onPopupClosed)
layer.addLayers(markers)
},
//websocket
connect() {
- const decolouringTimeout = 1000;
this.stompClient = new Stomp.client(`${import.meta.env.VITE_WS_BASE_URL}/update`, {debug: false});
this.stompClient.connect(
{},
- frame => this.subscribe(),
+ () => this.subscribe(),
error => {
console.log(error);
this.connect()
@@ -172,19 +171,19 @@ export default {
}
},
subscribe() {
- for (let key of this.layersToShow.keys()) {
- this.stompClient.subscribe("/broker/member/" + key, (member) => {
+ for (let collection of this.layersToShow.keys()) {
+ this.stompClient.subscribe("/broker/member/" + collection, (member) => {
let body = JSON.parse(member.body)
- let marker = useMarkers([body], (memberId) => this.memberId = memberId, this.onPopupClosed).at(0)
+ let marker = useMarkers([body], collection, (memberId) => this.memberId = memberId, this.onPopupClosed).at(0)
marker.setStyle({
color: '#FFA405',
})
- if (key === "gipod") {
+ if (collection === "gipod") {
setTimeout(function () {
this.updateMarker(marker)
}.bind(this), 1000)
}
- this.layers.get(key).addLayer(marker)
+ this.layers.get(collection).addLayer(marker)
});
}
},
@@ -253,4 +252,33 @@ export default {
left: 50%;
transform: translate(-50%, -50%);
}
+
+.popup-verkeersmeting {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 6px;
+}
+
+.popup-verkeersmeting-map-marker {
+ width: 16px;
+ height: 16px;
+}
+
+.popup-verkeersmeting-counting-row {
+ display: inline-flex;
+ align-items: end;
+ gap: 6px;
+ margin-top: -6px;
+}
+
+.popup-verkeersmeting-car-icon {
+ width: 24px;
+ height: 24px;
+}
+
+.popup-verkeersmeting-counting-result {
+ font-size: 14px;
+ line-height: 17px;
+}
\ No newline at end of file
diff --git a/frontend/src/components/map/composables/useMarkers.js b/frontend/src/components/map/composables/useMarkers.js
index 7ac8784..191071b 100644
--- a/frontend/src/components/map/composables/useMarkers.js
+++ b/frontend/src/components/map/composables/useMarkers.js
@@ -1,8 +1,9 @@
import L from "leaflet";
import marker from "../../../assets/svgs/legend/maps.marker.svg"
import selectedMarker from "../../../assets/svgs/legend/maps.marker-1.svg"
+import {usePopup} from "@/components/map/composables/usePopup";
-export function useMarkers(memberGeometries, onMarkerClicked, onPopupClosed) {
+export function useMarkers(memberGeometries, collection, onMarkerClicked, onPopupClosed) {
let icon = L.icon({
iconUrl: marker,
iconAnchor: [10, 28],
@@ -17,8 +18,9 @@ export function useMarkers(memberGeometries, onMarkerClicked, onPopupClosed) {
let markers = []
function onEachFeature(feature, layer) {
- if (feature.properties && feature.properties.popupContent) {
- let popup = L.popup().setContent(feature.properties.popupContent)
+ if (feature.properties?.popupProperties) {
+ let content = usePopup(collection, feature.properties.popupProperties)
+ let popup = L.popup().setContent(content)
popup.on("remove", () => {
if(layer.defaultOptions.icon) {
layer.setIcon(icon)
@@ -29,11 +31,11 @@ export function useMarkers(memberGeometries, onMarkerClicked, onPopupClosed) {
}
//bind click
layer.on({
- click: (event) => {
+ click: () => {
if(layer.defaultOptions.icon) {
layer.setIcon(selectedIcon)
}
- onMarkerClicked(event.sourceTarget._popup._content);
+ onMarkerClicked(feature.properties.memberId);
}
});
}
@@ -47,10 +49,11 @@ export function useMarkers(memberGeometries, onMarkerClicked, onPopupClosed) {
"type": "Feature",
"geometry": feature.geojsonGeometry,
"properties": {
- "popupContent": feature.memberId
+ "memberId": feature.memberId,
+ "popupProperties": feature.properties,
}
}
- let geoJson = L.geoJson(geoJsonFeature, {onEachFeature, pointToLayer: pointToLayer})
+ let geoJson = L.geoJson(geoJsonFeature, {onEachFeature: onEachFeature, pointToLayer: pointToLayer})
geoJson.setStyle({color: '#808080'});
markers.push(geoJson)
})
diff --git a/frontend/src/components/map/composables/usePopup.js b/frontend/src/components/map/composables/usePopup.js
new file mode 100644
index 0000000..e52f204
--- /dev/null
+++ b/frontend/src/components/map/composables/usePopup.js
@@ -0,0 +1,23 @@
+import mapsMarker from "../../../assets/svgs/legend/maps.marker.svg"
+import carIcon from "../../../assets/svgs/legend/car.svg"
+export function usePopup(collection, properties) {
+ switch (collection) {
+ case "gipod":
+ return `${properties.startTime}
`
+ case "verkeersmeting":
+ return ``
+ }
+}
+
+/**
+ * box-shadow: 0px 2px 12px 0px #6A768659;
+ *
+ * box-shadow: 0px 0px 1px 0px #CFD5DD;
+ */
\ No newline at end of file
diff --git a/frontend/src/components/slider/Slider.vue b/frontend/src/components/slider/Slider.vue
index f26a077..55735bd 100644
--- a/frontend/src/components/slider/Slider.vue
+++ b/frontend/src/components/slider/Slider.vue
@@ -54,10 +54,14 @@ function onShortcutClick(amount) {
}
function onRealTime() {
- sliderValue.value = maxSeconds;
- onPauseClick()
- emit('timestampChanged', now.getTime(), "PT10M")
- isRealtime.value = true
+ if(!isRealtime.value) {
+ sliderValue.value = maxSeconds;
+ onPauseClick()
+ // emit('timestampChanged', now.getTime(), "PT10M")
+ isRealtime.value = true
+ } else {
+ isRealtime.value = false;
+ }
}
function onPlayClick() {
diff --git a/gipod.config.yml b/gipod.config.yml
index 54489c8..2142aab 100644
--- a/gipod.config.yml
+++ b/gipod.config.yml
@@ -16,7 +16,7 @@ orchestrator:
- name: be.vlaanderen.informatievlaanderen.ldes.ldio.LdioHttpOut
config:
content-type: application/n-quads
- endpoint: http://demonstrator:8080/api/gipod/members
+ endpoint: http://host.docker.internal:8084/api/gipod/members
- name: verkeersmetingen-pipeline
description: "Simple http in, version creation, http out pipeline allowing to pause output."
input:
@@ -46,5 +46,5 @@ orchestrator:
repository-id: test
- name: be.vlaanderen.informatievlaanderen.ldes.ldio.LdioHttpOut
config:
- endpoint: http://demonstrator:8080/api/verkeersmeting/members
+ endpoint: http://host.docker.internal:8084/api/verkeersmeting/members
content-type: application/n-quads
\ No newline at end of file